From: Brian Aker Date: Wed, 16 Dec 2009 23:01:10 +0000 (-0800) Subject: Mass rename to simplify names. X-Git-Tag: 0.37~72 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=7c7750f02368b570353ea109f23a0ea26d226e02;hp=fa11f4a5d1a9c92eda13f8a5aa11dcff542b8815;p=m6w6%2Flibmemcached Mass rename to simplify names. Do you know how many times I get annoyed on a daily basis that I can't use tabs on the command line? Yeah... should have done this a while ago. --- diff --git a/libmemcached/Makefile.am b/libmemcached/Makefile.am index 80db3505..e85fecb7 100644 --- a/libmemcached/Makefile.am +++ b/libmemcached/Makefile.am @@ -1,91 +1,96 @@ -EXTRA_DIST = libmemcached.ver \ - libmemcached_probes.d \ - memcached/README.txt \ - memcached_configure.h.in \ - protocol/libmemcachedprotocol.ver \ - util/libmemcachedutil.ver +EXTRA_DIST = \ + libmemcached.ver \ + libmemcached_probes.d \ + memcached/README.txt \ + memcached_configure.h.in \ + protocol/libmemcachedprotocol.ver \ + util/libmemcachedutil.ver EXTRA_HEADERS = BUILT_SOURCES= noinst_HEADERS = libmemcached_probes.h \ - memcached_io.h \ - memcached_internal.h \ + io.h \ + internal.h \ common.h \ memcached/protocol_binary.h \ protocol/common.h \ protocol/ascii_handler.h \ protocol/binary_handler.h -pkginclude_HEADERS= memcached.h \ - memcached.hpp \ +pkginclude_HEADERS= \ + behavior.h \ + callback.h \ + constants.h \ exception.hpp \ - memcached_behavior.h \ - memcached_callback.h \ - memcached_configure.h \ - memcached_constants.h \ - memcached_get.h \ - memcached_result.h \ - memcached_server.h \ - memcached_storage.h \ - memcached_string.h \ - memcached_types.h \ - memcached_watchpoint.h \ - protocol_handler.h \ - visibility.h - -nobase_pkginclude_HEADERS=protocol/cache.h \ - protocol/callback.h - - -libmemcachedprotocol_la_SOURCES= protocol/ascii_handler.c \ - protocol/binary_handler.c \ - protocol/cache.c \ - protocol/pedantic.c \ - protocol/protocol_handler.c + get.h \ + memcached.h \ + memcached.hpp \ + memcached_configure.h \ + protocol_handler.h \ + result.h \ + server.h \ + storage.h \ + string.h \ + types.h \ + visibility.h \ + watchpoint.h + +nobase_pkginclude_HEADERS = \ + protocol/cache.h \ + protocol/callback.h + + +libmemcachedprotocol_la_SOURCES = \ + protocol/ascii_handler.c \ + protocol/binary_handler.c \ + protocol/cache.c \ + protocol/pedantic.c \ + protocol/protocol_handler.c libmemcachedprotocol_la_LDFLAGS= ${AM_LDFLAGS} -version-info 0:0:0 lib_LTLIBRARIES = libmemcached.la libmemcachedprotocol.la noinst_LTLIBRARIES = libmemcachedcallbacks.la libmemcachedcallbacks_la_CFLAGS = ${AM_CFLAGS} ${NO_STRICT_ALIASING} -libmemcachedcallbacks_la_SOURCES = memcached_callback.c +libmemcachedcallbacks_la_SOURCES = callback.c libmemcached_la_CFLAGS= ${AM_CFLAGS} ${NO_CONVERSION} -libmemcached_la_SOURCES = crc.c \ - memcached.c \ - memcached_auto.c \ - memcached_analyze.c \ - memcached_behavior.c \ - memcached_connect.c \ - memcached_delete.c \ - memcached_do.c \ - memcached_dump.c \ - memcached_fetch.c \ - memcached_flush.c \ - memcached_get.c \ - memcached_hash.c \ - memcached_hosts.c \ - memcached_io.c \ - memcached_purge.c \ - memcached_flush_buffers.c \ +libmemcached_la_SOURCES = \ + allocators.c \ + analyze.c \ + auto.c \ + behavior.c \ + connect.c \ + crc.c \ + delete.c \ + do.c \ + dump.c \ + fetch.c \ + flush.c \ + flush_buffers.c \ + get.c \ + hash.c \ + hosts.c \ + io.c \ + jenkins_hash.c \ + key.c \ md5.c \ - memcached_key.c \ - memcached_quit.c \ - memcached_parse.c \ - memcached_response.c \ - memcached_result.c \ - memcached_server.c \ - memcached_storage.c \ - memcached_string.c \ - memcached_stats.c \ - memcached_strerror.c \ - memcached_verbosity.c \ - memcached_version.c \ + memcached.c \ murmur_hash.c \ - jenkins_hash.c \ - memcached_allocators.c + parse.c \ + purge.c \ + quit.c \ + response.c \ + result.c \ + server.c \ + stats.c \ + storage.c \ + strerror.c \ + string.c \ + verbosity.c \ + version.c if INCLUDE_HSIEH_SRC @@ -97,11 +102,11 @@ libmemcached_la_LIBADD= $(LIBM) libmemcachedcallbacks.la libmemcached_la_LDFLAGS= ${AM_LDFLAGS} -version-info 3:0:0 if BUILD_LIBMEMCACHEDUTIL -pkginclude_HEADERS+= memcached_util.h memcached_pool.h +pkginclude_HEADERS+= memcached_util.h pool.h lib_LTLIBRARIES+= libmemcachedutil.la endif -libmemcachedutil_la_SOURCES= util/memcached_pool.c +libmemcachedutil_la_SOURCES= util/pool.c libmemcachedutil_la_LIBADD= libmemcached.la libmemcachedutil_la_LDFLAGS= ${AM_LDFLAGS} -version-info 0:0:0 libmemcachedutil_la_DEPENDENCIES=libmemcached.la diff --git a/libmemcached/allocators.c b/libmemcached/allocators.c new file mode 100644 index 00000000..767371cb --- /dev/null +++ b/libmemcached/allocators.c @@ -0,0 +1,72 @@ +#include "common.h" + +void libmemcached_free(memcached_st *ptr, void *mem) +{ + (void) ptr; + free(mem); +} + +void *libmemcached_malloc(memcached_st *ptr, size_t size) +{ + (void) ptr; + return malloc(size); +} + +void *libmemcached_realloc(memcached_st *ptr, void *mem, size_t size) +{ + (void) ptr; + return realloc(mem, size); +} + +void *libmemcached_calloc(memcached_st *ptr, size_t nelem, size_t size) +{ + if (ptr->call_malloc != libmemcached_malloc) + { + void *ret = libmemcached_malloc(ptr, nelem * size); + if (ret != NULL) + memset(ret, 0, nelem * size); + + return ret; + } + + return calloc(nelem, size); +} + +memcached_return_t memcached_set_memory_allocators(memcached_st *ptr, + memcached_malloc_fn mem_malloc, + memcached_free_fn mem_free, + memcached_realloc_fn mem_realloc, + memcached_calloc_fn mem_calloc) +{ + /* All should be set, or none should be set */ + if (mem_malloc == NULL && mem_free == NULL && mem_realloc == NULL && mem_calloc == NULL) + { + ptr->call_malloc= libmemcached_malloc; + ptr->call_free= libmemcached_free; + ptr->call_realloc= libmemcached_realloc; + ptr->call_calloc= libmemcached_calloc; + } + else if (mem_malloc == NULL || mem_free == NULL || mem_realloc == NULL || mem_calloc == NULL) + return MEMCACHED_FAILURE; + else + { + ptr->call_malloc= mem_malloc; + ptr->call_free= mem_free; + ptr->call_realloc= mem_realloc; + ptr->call_calloc= mem_calloc; + } + + return MEMCACHED_SUCCESS; +} + +void memcached_get_memory_allocators(memcached_st *ptr, + memcached_malloc_fn *mem_malloc, + memcached_free_fn *mem_free, + memcached_realloc_fn *mem_realloc, + memcached_calloc_fn *mem_calloc) +{ + *mem_malloc= ptr->call_malloc; + *mem_free= ptr->call_free; + *mem_realloc= ptr->call_realloc; + *mem_calloc= ptr->call_calloc; +} diff --git a/libmemcached/analyze.c b/libmemcached/analyze.c new file mode 100644 index 00000000..5c1c7e47 --- /dev/null +++ b/libmemcached/analyze.c @@ -0,0 +1,100 @@ +#include "common.h" + +static void calc_largest_consumption(memcached_analysis_st *result, + const uint32_t server_num, + const uint64_t nbytes) +{ + if (result->most_used_bytes < nbytes) + { + result->most_used_bytes= nbytes; + result->most_consumed_server= server_num; + } +} + +static void calc_oldest_node(memcached_analysis_st *result, + const uint32_t server_num, + const uint32_t uptime) +{ + if (result->longest_uptime < uptime) + { + result->longest_uptime= uptime; + result->oldest_server= server_num; + } +} + +static void calc_least_free_node(memcached_analysis_st *result, + const uint32_t server_num, + const uint64_t max_allowed_bytes, + const uint64_t used_bytes) +{ + uint64_t remaining_bytes= max_allowed_bytes - used_bytes; + + if (result->least_remaining_bytes == 0 || + remaining_bytes < result->least_remaining_bytes) + { + result->least_remaining_bytes= remaining_bytes; + result->least_free_server= server_num; + } +} + +static void calc_average_item_size(memcached_analysis_st *result, + const uint64_t total_items, + const uint64_t total_bytes) +{ + if (total_items > 0 && total_bytes > 0) + result->average_item_size= (uint32_t) (total_bytes / total_items); +} + +static void calc_hit_ratio(memcached_analysis_st *result, + const uint64_t total_get_hits, + const uint64_t total_get_cmds) +{ + if (total_get_hits == 0 || total_get_cmds == 0) + { + result->pool_hit_ratio= 0; + return; + } + + double temp= (double) (total_get_hits/total_get_cmds); + result->pool_hit_ratio= temp * 100; +} + +memcached_analysis_st *memcached_analyze(memcached_st *memc, + memcached_stat_st *memc_stat, + memcached_return_t *error) +{ + uint64_t total_items= 0, total_bytes= 0; + uint64_t total_get_cmds= 0, total_get_hits= 0; + uint32_t server_count, x; + memcached_analysis_st *result; + + *error= MEMCACHED_SUCCESS; + server_count= memcached_server_count(memc); + result= (memcached_analysis_st*)calloc(memc->number_of_hosts, + sizeof(memcached_analysis_st)); + + if (!result) + { + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + + for (x= 0; x < server_count; x++) + { + calc_largest_consumption(result, x, memc_stat[x].bytes); + calc_oldest_node(result, x, memc_stat[x].uptime); + calc_least_free_node(result, x, + memc_stat[x].limit_maxbytes, + memc_stat[x].bytes); + + total_get_hits+= memc_stat[x].get_hits; + total_get_cmds+= memc_stat[x].cmd_get; + total_items+= memc_stat[x].curr_items; + total_bytes+= memc_stat[x].bytes; + } + + calc_average_item_size(result, total_items, total_bytes); + calc_hit_ratio(result, total_get_hits, total_get_cmds); + + return result; +} diff --git a/libmemcached/auto.c b/libmemcached/auto.c new file mode 100644 index 00000000..b7466404 --- /dev/null +++ b/libmemcached/auto.c @@ -0,0 +1,275 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Methods for adding or decrementing values from an object in memcached + * + */ + +#include "common.h" + +static memcached_return_t memcached_auto(memcached_st *ptr, + const char *verb, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + uint64_t offset, + uint64_t *value) +{ + size_t send_length; + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + unsigned int server_key; + bool no_reply= ptr->flags.no_reply; + + unlikely (ptr->hosts == NULL || ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + return MEMCACHED_BAD_KEY_PROVIDED; + + server_key= memcached_generate_hash(ptr, master_key, master_key_length); + + send_length= (size_t)snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%s %s%.*s %" PRIu64 "%s\r\n", verb, + ptr->prefix_key, + (int)key_length, key, + offset, no_reply ? " noreply" : ""); + unlikely (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) + return MEMCACHED_WRITE_FAILURE; + + rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, 1); + if (no_reply || rc != MEMCACHED_SUCCESS) + return rc; + + rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + /* + So why recheck responce? Because the protocol is brain dead :) + The number returned might end up equaling one of the string + values. Less chance of a mistake with strncmp() so we will + use it. We still called memcached_response() though since it + worked its magic for non-blocking IO. + */ + if (!strncmp(buffer, "ERROR\r\n", 7)) + { + *value= 0; + rc= MEMCACHED_PROTOCOL_ERROR; + } + else if (!strncmp(buffer, "NOT_FOUND\r\n", 11)) + { + *value= 0; + rc= MEMCACHED_NOTFOUND; + } + else + { + *value= strtoull(buffer, (char **)NULL, 10); + rc= MEMCACHED_SUCCESS; + } + + return rc; +} + +static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + uint64_t offset, uint64_t initial, + uint32_t expiration, + uint64_t *value) +{ + unsigned int server_key; + bool no_reply= ptr->flags.no_reply; + + unlikely (ptr->hosts == NULL || ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + server_key= memcached_generate_hash(ptr, master_key, master_key_length); + + if (no_reply) + { + if(cmd == PROTOCOL_BINARY_CMD_DECREMENT) + cmd= PROTOCOL_BINARY_CMD_DECREMENTQ; + if(cmd == PROTOCOL_BINARY_CMD_INCREMENT) + cmd= PROTOCOL_BINARY_CMD_INCREMENTQ; + } + protocol_binary_request_incr request= {.bytes= {0}}; + + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= cmd; + request.message.header.request.keylen= htons((uint16_t) key_length); + request.message.header.request.extlen= 20; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl((uint32_t) (key_length + request.message.header.request.extlen)); + request.message.body.delta= htonll(offset); + request.message.body.initial= htonll(initial); + request.message.body.expiration= htonl((uint32_t) expiration); + + if ((memcached_do(&ptr->hosts[server_key], request.bytes, + sizeof(request.bytes), 0)!=MEMCACHED_SUCCESS) || + (memcached_io_write(&ptr->hosts[server_key], key, key_length, 1) == -1)) + { + memcached_io_reset(&ptr->hosts[server_key]); + return MEMCACHED_WRITE_FAILURE; + } + + if (no_reply) + return MEMCACHED_SUCCESS; + return memcached_response(&ptr->hosts[server_key], (char*)value, sizeof(*value), NULL); +} + +memcached_return_t memcached_increment(memcached_st *ptr, + const char *key, size_t key_length, + uint32_t offset, + uint64_t *value) +{ + return memcached_increment_by_key(ptr, key, key_length, key, key_length, offset, value); +} + +memcached_return_t memcached_decrement(memcached_st *ptr, + const char *key, size_t key_length, + uint32_t offset, + uint64_t *value) +{ + return memcached_decrement_by_key(ptr, key, key_length, key, key_length, offset, value); +} + +memcached_return_t memcached_increment_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + uint64_t offset, + uint64_t *value) +{ + memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + LIBMEMCACHED_MEMCACHED_INCREMENT_START(); + if (ptr->flags.binary_protocol) + { + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, + master_key, master_key_length, key, key_length, + (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, + value); + } + else + { + rc= memcached_auto(ptr, "incr", master_key, master_key_length, key, key_length, offset, value); + } + + LIBMEMCACHED_MEMCACHED_INCREMENT_END(); + + return rc; +} + +memcached_return_t memcached_decrement_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + uint64_t offset, + uint64_t *value) +{ + memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + LIBMEMCACHED_MEMCACHED_DECREMENT_START(); + if (ptr->flags.binary_protocol) + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, + master_key, master_key_length, key, key_length, + (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, + value); + else + rc= memcached_auto(ptr, "decr", master_key, master_key_length, key, key_length, offset, value); + + LIBMEMCACHED_MEMCACHED_DECREMENT_END(); + + return rc; +} + +memcached_return_t memcached_increment_with_initial(memcached_st *ptr, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + return memcached_increment_with_initial_by_key(ptr, key, key_length, + key, key_length, + offset, initial, expiration, value); +} + +memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); + if (ptr->flags.binary_protocol) + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, + master_key, master_key_length, key, key_length, + offset, initial, (uint32_t)expiration, + value); + else + rc= MEMCACHED_PROTOCOL_ERROR; + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); + + return rc; +} + +memcached_return_t memcached_decrement_with_initial(memcached_st *ptr, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + return memcached_decrement_with_initial_by_key(ptr, key, key_length, + key, key_length, + offset, initial, expiration, value); +} + +memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char *key, + size_t key_length, + uint64_t offset, + uint64_t initial, + time_t expiration, + uint64_t *value) +{ + memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); + if (ptr->flags.binary_protocol) + { + rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, + master_key, master_key_length, key, key_length, + offset, initial, (uint32_t)expiration, + value); + } + else + { + rc= MEMCACHED_PROTOCOL_ERROR; + } + + LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); + + return rc; +} + diff --git a/libmemcached/behavior.c b/libmemcached/behavior.c new file mode 100644 index 00000000..37c9f529 --- /dev/null +++ b/libmemcached/behavior.c @@ -0,0 +1,314 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Change the behavior of the memcached connection. + * + */ + +#include "common.h" +#include +#include +#include +#include + +/* + This function is used to modify the behavior of running client. + + We quit all connections so we can reset the sockets. +*/ + +memcached_return_t memcached_behavior_set(memcached_st *ptr, + memcached_behavior_t flag, + uint64_t data) +{ + switch (flag) + { + case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: + ptr->number_of_replicas= (uint32_t)data; + break; + case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: + ptr->io_msg_watermark= (uint32_t) data; + break; + case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: + ptr->io_bytes_watermark= (uint32_t)data; + break; + case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: + ptr->io_key_prefetch = (uint32_t)data; + break; + case MEMCACHED_BEHAVIOR_SND_TIMEOUT: + ptr->snd_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: + ptr->rcv_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: + ptr->server_failure_limit= (uint32_t)data; + break; + case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: + if (data) + ptr->flags.verify_key= false; + + ptr->flags.binary_protocol= data ? true : false; + break; + case MEMCACHED_BEHAVIOR_SUPPORT_CAS: + ptr->flags.support_cas= data ? true: false; + break; + case MEMCACHED_BEHAVIOR_NO_BLOCK: + ptr->flags.no_block= data ? true: false; + memcached_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: + ptr->flags.buffer_requests= data ? true : false; + memcached_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_USE_UDP: + if (ptr->number_of_hosts) + return MEMCACHED_FAILURE; + ptr->flags.use_udp= data ? true : false; + + if (data) + ptr->flags.no_reply= data ? true : false; + break; + + case MEMCACHED_BEHAVIOR_TCP_NODELAY: + ptr->flags.tcp_nodelay= data ? true : false; + memcached_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_DISTRIBUTION: + { + ptr->distribution= (memcached_server_distribution_t)(data); + if (ptr->distribution == MEMCACHED_DISTRIBUTION_RANDOM) + { + srandom((uint32_t) time(NULL)); + } + run_distribution(ptr); + break; + } + case MEMCACHED_BEHAVIOR_KETAMA: + { + if (data) + { + ptr->hash= MEMCACHED_HASH_MD5; + ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA; + } + else + { + ptr->hash= 0; + ptr->distribution= 0; + } + run_distribution(ptr); + break; + } + case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: + { + ptr->hash= MEMCACHED_HASH_MD5; + ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA; + ptr->flags.ketama_weighted= data ? true : false; + run_distribution(ptr); + break; + } + case MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE: + switch (data) + { + case MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED: + ptr->hash= MEMCACHED_HASH_MD5; + ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA; + break; + case MEMCACHED_KETAMA_COMPAT_SPY: + ptr->hash= MEMCACHED_HASH_MD5; + ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY; + break; + default: + return MEMCACHED_FAILURE; + } + run_distribution(ptr); + break; + case MEMCACHED_BEHAVIOR_HASH: +#ifndef HAVE_HSIEH_HASH + if ((memcached_hash_t)(data) == MEMCACHED_HASH_HSIEH) + return MEMCACHED_FAILURE; +#endif + ptr->hash= (memcached_hash_t)(data); + break; + case MEMCACHED_BEHAVIOR_KETAMA_HASH: + ptr->hash_continuum= (memcached_hash_t)(data); + run_distribution(ptr); + break; + case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: + ptr->flags.use_cache_lookups= data ? true : false; + memcached_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_VERIFY_KEY: + if (ptr->flags.binary_protocol) + break; + ptr->flags.verify_key= data ? true : false; + break; + case MEMCACHED_BEHAVIOR_SORT_HOSTS: + { + ptr->flags.use_sort_hosts= data ? true : false; + run_distribution(ptr); + + break; + } + case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: + ptr->poll_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: + ptr->connect_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: + ptr->retry_timeout= (int32_t)data; + break; + case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: + ptr->send_size= (int32_t)data; + memcached_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: + ptr->recv_size= (int32_t)data; + memcached_quit(ptr); + break; + case MEMCACHED_BEHAVIOR_USER_DATA: + return MEMCACHED_FAILURE; + case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: + ptr->flags.hash_with_prefix_key= data ? true : false; + break; + case MEMCACHED_BEHAVIOR_NOREPLY: + ptr->flags.no_reply= data ? true : false; + break; + case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: + ptr->flags.auto_eject_hosts= data ? true : false; + break; + case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: + srandom((uint32_t) time(NULL)); + ptr->flags.randomize_replica_read= data ? true : false; + break; + default: + /* Shouldn't get here */ + WATCHPOINT_ASSERT(flag); + break; + } + + return MEMCACHED_SUCCESS; +} + +uint64_t memcached_behavior_get(memcached_st *ptr, + memcached_behavior_t flag) +{ + switch (flag) + { + case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: + return ptr->number_of_replicas; + case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: + return ptr->io_msg_watermark; + case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: + return ptr->io_bytes_watermark; + case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: + return ptr->io_key_prefetch; + case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: + return ptr->flags.binary_protocol; + case MEMCACHED_BEHAVIOR_SUPPORT_CAS: + return ptr->flags.support_cas; + case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: + return ptr->flags.use_cache_lookups; + case MEMCACHED_BEHAVIOR_NO_BLOCK: + return ptr->flags.no_block; + case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: + return ptr->flags.buffer_requests; + case MEMCACHED_BEHAVIOR_USE_UDP: + return ptr->flags.use_udp; + case MEMCACHED_BEHAVIOR_TCP_NODELAY: + return ptr->flags.tcp_nodelay; + case MEMCACHED_BEHAVIOR_VERIFY_KEY: + return ptr->flags.verify_key; + case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: + return ptr->flags.ketama_weighted; + case MEMCACHED_BEHAVIOR_DISTRIBUTION: + return ptr->distribution; + case MEMCACHED_BEHAVIOR_KETAMA: + return (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA) ? (uint64_t) 1 : 0; + case MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE: + switch (ptr->distribution) + { + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: + return MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED; + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: + return MEMCACHED_KETAMA_COMPAT_SPY; + case MEMCACHED_DISTRIBUTION_MODULA: + case MEMCACHED_DISTRIBUTION_CONSISTENT: + case MEMCACHED_DISTRIBUTION_RANDOM: + default: + return (uint64_t)-1; + } + /* NOTREACHED */ + case MEMCACHED_BEHAVIOR_HASH: + return ptr->hash; + case MEMCACHED_BEHAVIOR_KETAMA_HASH: + return ptr->hash_continuum; + case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: + return ptr->server_failure_limit; + case MEMCACHED_BEHAVIOR_SORT_HOSTS: + return ptr->flags.use_sort_hosts; + case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: + return (uint64_t)ptr->poll_timeout; + case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: + return (uint64_t)ptr->connect_timeout; + case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: + return (uint64_t)ptr->retry_timeout; + case MEMCACHED_BEHAVIOR_SND_TIMEOUT: + return (uint64_t)ptr->snd_timeout; + case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: + return (uint64_t)ptr->rcv_timeout; + case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: + { + int sock_size; + socklen_t sock_length= sizeof(int); + + /* REFACTOR */ + /* We just try the first host, and if it is down we return zero */ + if ((memcached_connect(&ptr->hosts[0])) != MEMCACHED_SUCCESS) + return 0; + + if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET, + SO_SNDBUF, &sock_size, &sock_length)) + return 0; /* Zero means error */ + + return (uint64_t) sock_size; + } + case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: + { + int sock_size; + socklen_t sock_length= sizeof(int); + + /* REFACTOR */ + /* We just try the first host, and if it is down we return zero */ + if ((memcached_connect(&ptr->hosts[0])) != MEMCACHED_SUCCESS) + return 0; + + if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET, + SO_RCVBUF, &sock_size, &sock_length)) + return 0; /* Zero means error */ + + return (uint64_t) sock_size; + } + case MEMCACHED_BEHAVIOR_USER_DATA: + return MEMCACHED_FAILURE; + case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: + return ptr->flags.hash_with_prefix_key; + case MEMCACHED_BEHAVIOR_NOREPLY: + return ptr->flags.no_reply; + case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: + return ptr->flags.auto_eject_hosts; + case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: + return ptr->flags.randomize_replica_read; + default: + WATCHPOINT_ASSERT(flag); + break; + } + + WATCHPOINT_ASSERT(0); /* Programming mistake if it gets this far */ + return 0; +} diff --git a/libmemcached/behavior.h b/libmemcached/behavior.h new file mode 100644 index 00000000..14cbc3aa --- /dev/null +++ b/libmemcached/behavior.h @@ -0,0 +1,30 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Change the behavior of the memcached connection. + * + */ + +#ifndef __MEMCACHED_BEHAVIOR_H__ +#define __MEMCACHED_BEHAVIOR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +LIBMEMCACHED_API +memcached_return_t memcached_behavior_set(memcached_st *ptr, memcached_behavior_t flag, uint64_t data); + +LIBMEMCACHED_API +uint64_t memcached_behavior_get(memcached_st *ptr, memcached_behavior_t flag); + + +#ifdef __cplusplus +} +#endif + +#endif /* __MEMCACHED_BEHAVIOR_H__ */ diff --git a/libmemcached/callback.c b/libmemcached/callback.c new file mode 100644 index 00000000..b0ddfc2b --- /dev/null +++ b/libmemcached/callback.c @@ -0,0 +1,186 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Change any of the possible callbacks. + * + */ + +#include "common.h" +#include +#include +#include + +/* + These functions provide data and function callback support +*/ + +memcached_return_t memcached_callback_set(memcached_st *ptr, + memcached_callback_t flag, + void *data) +{ + switch (flag) + { + case MEMCACHED_CALLBACK_PREFIX_KEY: + { + char *key= (char *)data; + + if (key) + { + size_t key_length= strlen(key); + + if (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED) + { + return MEMCACHED_BAD_KEY_PROVIDED; + } + + if ((key_length > MEMCACHED_PREFIX_KEY_MAX_SIZE -1) + || (strcpy(ptr->prefix_key, key) == NULL)) + { + ptr->prefix_key_length= 0; + return MEMCACHED_BAD_KEY_PROVIDED; + } + else + { + ptr->prefix_key_length= key_length; + } + } + else + { + memset(ptr->prefix_key, 0, MEMCACHED_PREFIX_KEY_MAX_SIZE); + ptr->prefix_key_length= 0; + } + + break; + } + case MEMCACHED_CALLBACK_USER_DATA: + { + ptr->user_data= data; + break; + } + case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: + { + memcached_cleanup_fn func= *(memcached_cleanup_fn *)&data; + ptr->on_cleanup= func; + break; + } + case MEMCACHED_CALLBACK_CLONE_FUNCTION: + { + memcached_clone_fn func= *(memcached_clone_fn *)&data; + ptr->on_clone= func; + break; + } +#ifdef MEMCACHED_ENABLE_DEPRECATED + case MEMCACHED_CALLBACK_MALLOC_FUNCTION: + { + memcached_malloc_function func= *(memcached_malloc_fn *)&data; + ptr->call_malloc= func; + break; + } + case MEMCACHED_CALLBACK_REALLOC_FUNCTION: + { + memcached_realloc_function func= *(memcached_realloc_fn *)&data; + ptr->call_realloc= func; + break; + } + case MEMCACHED_CALLBACK_FREE_FUNCTION: + { + memcached_free_function func= *(memcached_free_fn *)&data; + ptr->call_free= func; + break; + } +#endif + case MEMCACHED_CALLBACK_GET_FAILURE: + { + memcached_trigger_key_fn func= *(memcached_trigger_key_fn *)&data; + ptr->get_key_failure= func; + break; + } + case MEMCACHED_CALLBACK_DELETE_TRIGGER: + { + memcached_trigger_delete_key_fn func= *(memcached_trigger_delete_key_fn *)&data; + ptr->delete_trigger= func; + break; + } + default: + return MEMCACHED_FAILURE; + } + + return MEMCACHED_SUCCESS; +} + +void *memcached_callback_get(memcached_st *ptr, + memcached_callback_t flag, + memcached_return_t *error) +{ + memcached_return_t local_error; + + if (!error) + error = &local_error; + + switch (flag) + { + case MEMCACHED_CALLBACK_PREFIX_KEY: + { + if (ptr->prefix_key[0] == 0) + { + *error= MEMCACHED_FAILURE; + return NULL; + } + else + { + *error= MEMCACHED_SUCCESS; + return (void *)ptr->prefix_key; + } + } + case MEMCACHED_CALLBACK_USER_DATA: + { + *error= ptr->user_data ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return (void *)ptr->user_data; + } + case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: + { + *error= ptr->on_cleanup ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->on_cleanup; + } + case MEMCACHED_CALLBACK_CLONE_FUNCTION: + { + *error= ptr->on_clone ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->on_clone; + } +#ifdef MEMCACHED_ENABLE_DEPRECATED + case MEMCACHED_CALLBACK_MALLOC_FUNCTION: + { + *error= ptr->call_malloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->call_malloc; + } + case MEMCACHED_CALLBACK_REALLOC_FUNCTION: + { + *error= ptr->call_realloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->call_realloc; + } + case MEMCACHED_CALLBACK_FREE_FUNCTION: + { + *error= ptr->call_free ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->call_free; + } +#endif + case MEMCACHED_CALLBACK_GET_FAILURE: + { + *error= ptr->get_key_failure ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->get_key_failure; + } + case MEMCACHED_CALLBACK_DELETE_TRIGGER: + { + *error= ptr->delete_trigger ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; + return *(void **)&ptr->delete_trigger; + } + default: + WATCHPOINT_ASSERT(0); + *error= MEMCACHED_FAILURE; + return NULL; + } +} diff --git a/libmemcached/callback.h b/libmemcached/callback.h new file mode 100644 index 00000000..0719a66f --- /dev/null +++ b/libmemcached/callback.h @@ -0,0 +1,32 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Change any of the possible callbacks. + * + */ + +#ifndef __MEMCACHED_CALLBACK_H__ +#define __MEMCACHED_CALLBACK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +LIBMEMCACHED_API +memcached_return_t memcached_callback_set(memcached_st *ptr, + memcached_callback_t flag, + void *data); +LIBMEMCACHED_API +void *memcached_callback_get(memcached_st *ptr, + memcached_callback_t flag, + memcached_return_t *error); + +#ifdef __cplusplus +} +#endif + +#endif /* __MEMCACHED_CALLBACK_H__ */ diff --git a/libmemcached/common.h b/libmemcached/common.h index 3a08ffca..c9d7cf81 100644 --- a/libmemcached/common.h +++ b/libmemcached/common.h @@ -52,11 +52,11 @@ #include "libmemcached/memcached.h" -#include "libmemcached/memcached_watchpoint.h" +#include "libmemcached/watchpoint.h" /* These are private not to be installed headers */ -#include "libmemcached/memcached_io.h" -#include "libmemcached/memcached_internal.h" +#include "libmemcached/io.h" +#include "libmemcached/internal.h" #include "libmemcached/libmemcached_probes.h" #include "libmemcached/memcached/protocol_binary.h" #include "libmemcached/byteorder.h" diff --git a/libmemcached/connect.c b/libmemcached/connect.c new file mode 100644 index 00000000..24328f4c --- /dev/null +++ b/libmemcached/connect.c @@ -0,0 +1,355 @@ +#include "common.h" +#include +#include +#include + +static memcached_return_t set_hostinfo(memcached_server_st *server) +{ + struct addrinfo *ai; + struct addrinfo hints; + int e; + char str_port[NI_MAXSERV]; + + sprintf(str_port, "%u", server->port); + + memset(&hints, 0, sizeof(hints)); + + // hints.ai_family= AF_INET; + if (server->type == MEMCACHED_CONNECTION_UDP) + { + hints.ai_protocol= IPPROTO_UDP; + hints.ai_socktype= SOCK_DGRAM; + } + else + { + hints.ai_socktype= SOCK_STREAM; + hints.ai_protocol= IPPROTO_TCP; + } + + e= getaddrinfo(server->hostname, str_port, &hints, &ai); + if (e != 0) + { + WATCHPOINT_STRING(server->hostname); + WATCHPOINT_STRING(gai_strerror(e)); + return MEMCACHED_HOST_LOOKUP_FAILURE; + } + + if (server->address_info) + { + freeaddrinfo(server->address_info); + server->address_info= NULL; + } + server->address_info= ai; + + return MEMCACHED_SUCCESS; +} + +static memcached_return_t set_socket_options(memcached_server_st *ptr) +{ + WATCHPOINT_ASSERT(ptr->fd != -1); + + if (ptr->type == MEMCACHED_CONNECTION_UDP) + return MEMCACHED_SUCCESS; + +#ifdef HAVE_SNDTIMEO + if (ptr->root->snd_timeout) + { + int error; + struct timeval waittime; + + waittime.tv_sec= 0; + waittime.tv_usec= ptr->root->snd_timeout; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, + &waittime, (socklen_t)sizeof(struct timeval)); + WATCHPOINT_ASSERT(error == 0); + } +#endif + +#ifdef HAVE_RCVTIMEO + if (ptr->root->rcv_timeout) + { + int error; + struct timeval waittime; + + waittime.tv_sec= 0; + waittime.tv_usec= ptr->root->rcv_timeout; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, + &waittime, (socklen_t)sizeof(struct timeval)); + WATCHPOINT_ASSERT(error == 0); + } +#endif + + if (ptr->root->flags.no_block) + { + int error; + struct linger linger; + + linger.l_onoff= 1; + linger.l_linger= 0; /* By default on close() just drop the socket */ + error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, + &linger, (socklen_t)sizeof(struct linger)); + WATCHPOINT_ASSERT(error == 0); + } + + if (ptr->root->flags.tcp_nodelay) + { + int flag= 1; + int error; + + error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, + &flag, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + } + + if (ptr->root->send_size) + { + int error; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, + &ptr->root->send_size, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + } + + if (ptr->root->recv_size) + { + int error; + + error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF, + &ptr->root->recv_size, (socklen_t)sizeof(int)); + WATCHPOINT_ASSERT(error == 0); + } + + /* libmemcached will always use nonblocking IO to avoid write deadlocks */ + int flags; + + do + flags= fcntl(ptr->fd, F_GETFL, 0); + while (flags == -1 && (errno == EINTR || errno == EAGAIN)); + + unlikely (flags == -1) + return MEMCACHED_CONNECTION_FAILURE; + else if ((flags & O_NONBLOCK) == 0) + { + int rval; + + do + rval= fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK); + while (rval == -1 && (errno == EINTR || errno == EAGAIN)); + + unlikely (rval == -1) + return MEMCACHED_CONNECTION_FAILURE; + } + + return MEMCACHED_SUCCESS; +} + +static memcached_return_t unix_socket_connect(memcached_server_st *ptr) +{ + struct sockaddr_un servAddr; + socklen_t addrlen; + + if (ptr->fd == -1) + { + if ((ptr->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + ptr->cached_errno= errno; + return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; + } + + memset(&servAddr, 0, sizeof (struct sockaddr_un)); + servAddr.sun_family= AF_UNIX; + strcpy(servAddr.sun_path, ptr->hostname); /* Copy filename */ + + addrlen= (socklen_t) (strlen(servAddr.sun_path) + sizeof(servAddr.sun_family)); + +test_connect: + if (connect(ptr->fd, + (struct sockaddr *)&servAddr, + sizeof(servAddr)) < 0) + { + switch (errno) + { + case EINPROGRESS: + case EALREADY: + case EINTR: + goto test_connect; + case EISCONN: /* We were spinning waiting on connect */ + break; + default: + WATCHPOINT_ERRNO(errno); + ptr->cached_errno= errno; + return MEMCACHED_ERRNO; + } + } + } + + WATCHPOINT_ASSERT(ptr->fd != -1); + return MEMCACHED_SUCCESS; +} + +static memcached_return_t network_connect(memcached_server_st *ptr) +{ + if (ptr->fd == -1) + { + struct addrinfo *use; + + if (!ptr->sockaddr_inited || + (!(ptr->root->flags.use_cache_lookups))) + { + memcached_return_t rc; + + rc= set_hostinfo(ptr); + if (rc != MEMCACHED_SUCCESS) + return rc; + ptr->sockaddr_inited= true; + } + + use= ptr->address_info; + /* Create the socket */ + while (use != NULL) + { + /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */ + if (ptr->type == MEMCACHED_CONNECTION_UDP && use->ai_family != AF_INET) + { + use= use->ai_next; + continue; + } + + if ((ptr->fd= socket(use->ai_family, + use->ai_socktype, + use->ai_protocol)) < 0) + { + ptr->cached_errno= errno; + WATCHPOINT_ERRNO(errno); + return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; + } + + (void)set_socket_options(ptr); + + /* connect to server */ + while (ptr->fd != -1 && + connect(ptr->fd, use->ai_addr, use->ai_addrlen) < 0) + { + ptr->cached_errno= errno; + if (errno == EINPROGRESS || /* nonblocking mode - first return, */ + errno == EALREADY) /* nonblocking mode - subsequent returns */ + { + struct pollfd fds[1]; + fds[0].fd = ptr->fd; + fds[0].events = POLLOUT; + int error= poll(fds, 1, ptr->root->connect_timeout); + + if (error != 1 || fds[0].revents & POLLERR) + { + if (fds[0].revents & POLLERR) + { + int err; + socklen_t len = sizeof (err); + (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); + ptr->cached_errno= (err == 0) ? errno : err; + } + + (void)close(ptr->fd); + ptr->fd= -1; + } + } + else if (errno == EISCONN) /* we are connected :-) */ + { + break; + } + else if (errno != EINTR) + { + (void)close(ptr->fd); + ptr->fd= -1; + break; + } + } + + if (ptr->fd != -1) + { + WATCHPOINT_ASSERT(ptr->cursor_active == 0); + ptr->server_failure_counter= 0; + return MEMCACHED_SUCCESS; + } + use = use->ai_next; + } + } + + if (ptr->fd == -1) + { + /* Failed to connect. schedule next retry */ + if (ptr->root->retry_timeout) + { + struct timeval next_time; + + if (gettimeofday(&next_time, NULL) == 0) + ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout; + } + ptr->server_failure_counter++; + if (ptr->cached_errno == 0) + return MEMCACHED_TIMEOUT; + + return MEMCACHED_ERRNO; /* The last error should be from connect() */ + } + + ptr->server_failure_counter= 0; + return MEMCACHED_SUCCESS; /* The last error should be from connect() */ +} + + +memcached_return_t memcached_connect(memcached_server_st *ptr) +{ + memcached_return_t rc= MEMCACHED_NO_SERVERS; + LIBMEMCACHED_MEMCACHED_CONNECT_START(); + + /* both retry_timeout and server_failure_limit must be set in order to delay retrying a server on error. */ + WATCHPOINT_ASSERT(ptr->root); + if (ptr->root->retry_timeout && ptr->root->server_failure_limit) + { + struct timeval curr_time; + + gettimeofday(&curr_time, NULL); + + /* if we've had too many consecutive errors on this server, mark it dead. */ + if (ptr->server_failure_counter >= ptr->root->server_failure_limit) + { + ptr->next_retry= curr_time.tv_sec + ptr->root->retry_timeout; + ptr->server_failure_counter= 0; + } + + if (curr_time.tv_sec < ptr->next_retry) + { + if (memcached_behavior_get(ptr->root, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS)) + run_distribution(ptr->root); + + ptr->root->last_disconnected_server = ptr; + return MEMCACHED_SERVER_MARKED_DEAD; + } + } + + /* We need to clean up the multi startup piece */ + switch (ptr->type) + { + case MEMCACHED_CONNECTION_UNKNOWN: + WATCHPOINT_ASSERT(0); + rc= MEMCACHED_NOT_SUPPORTED; + break; + case MEMCACHED_CONNECTION_UDP: + case MEMCACHED_CONNECTION_TCP: + rc= network_connect(ptr); + break; + case MEMCACHED_CONNECTION_UNIX_SOCKET: + rc= unix_socket_connect(ptr); + break; + default: + WATCHPOINT_ASSERT(0); + } + + unlikely ( rc != MEMCACHED_SUCCESS) ptr->root->last_disconnected_server = ptr; + + LIBMEMCACHED_MEMCACHED_CONNECT_END(); + + return rc; +} diff --git a/libmemcached/constants.h b/libmemcached/constants.h new file mode 100644 index 00000000..cbdbf4b5 --- /dev/null +++ b/libmemcached/constants.h @@ -0,0 +1,154 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Constants for libmemcached + * + */ + +#ifndef __MEMCACHED_CONSTANTS_H__ +#define __MEMCACHED_CONSTANTS_H__ + +/* Public defines */ +#define MEMCACHED_DEFAULT_PORT 11211 +#define MEMCACHED_MAX_KEY 251 /* We add one to have it null terminated */ +#define MEMCACHED_MAX_BUFFER 8196 +#define MEMCACHED_MAX_HOST_LENGTH 64 +#define MEMCACHED_MAX_HOST_SORT_LENGTH 86 /* Used for Ketama */ +#define MEMCACHED_POINTS_PER_SERVER 100 +#define MEMCACHED_POINTS_PER_SERVER_KETAMA 160 +#define MEMCACHED_CONTINUUM_SIZE MEMCACHED_POINTS_PER_SERVER*100 /* This would then set max hosts to 100 */ +#define MEMCACHED_STRIDE 4 +#define MEMCACHED_DEFAULT_TIMEOUT 1000 +#define MEMCACHED_CONTINUUM_ADDITION 10 /* How many extra slots we should build for in the continuum */ +#define MEMCACHED_PREFIX_KEY_MAX_SIZE 128 +#define MEMCACHED_EXPIRATION_NOT_ADD 0xffffffffU + +typedef enum { + MEMCACHED_SUCCESS, + MEMCACHED_FAILURE, + MEMCACHED_HOST_LOOKUP_FAILURE, + MEMCACHED_CONNECTION_FAILURE, + MEMCACHED_CONNECTION_BIND_FAILURE, + MEMCACHED_WRITE_FAILURE, + MEMCACHED_READ_FAILURE, + MEMCACHED_UNKNOWN_READ_FAILURE, + MEMCACHED_PROTOCOL_ERROR, + MEMCACHED_CLIENT_ERROR, + MEMCACHED_SERVER_ERROR, + MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE, + MEMCACHED_DATA_EXISTS, + MEMCACHED_DATA_DOES_NOT_EXIST, + MEMCACHED_NOTSTORED, + MEMCACHED_STORED, + MEMCACHED_NOTFOUND, + MEMCACHED_MEMORY_ALLOCATION_FAILURE, + MEMCACHED_PARTIAL_READ, + MEMCACHED_SOME_ERRORS, + MEMCACHED_NO_SERVERS, + MEMCACHED_END, + MEMCACHED_DELETED, + MEMCACHED_VALUE, + MEMCACHED_STAT, + MEMCACHED_ITEM, + MEMCACHED_ERRNO, + MEMCACHED_FAIL_UNIX_SOCKET, + MEMCACHED_NOT_SUPPORTED, + MEMCACHED_NO_KEY_PROVIDED, /* Deprecated. Use MEMCACHED_BAD_KEY_PROVIDED! */ + MEMCACHED_FETCH_NOTFINISHED, + MEMCACHED_TIMEOUT, + MEMCACHED_BUFFERED, + MEMCACHED_BAD_KEY_PROVIDED, + MEMCACHED_INVALID_HOST_PROTOCOL, + MEMCACHED_SERVER_MARKED_DEAD, + MEMCACHED_UNKNOWN_STAT_KEY, + MEMCACHED_E2BIG, + MEMCACHED_INVALID_ARGUMENTS, + MEMCACHED_MAXIMUM_RETURN /* Always add new error code before */ +} memcached_return_t; + + +typedef enum { + MEMCACHED_DISTRIBUTION_MODULA, + MEMCACHED_DISTRIBUTION_CONSISTENT, + MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA, + MEMCACHED_DISTRIBUTION_RANDOM, + MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY +} memcached_server_distribution_t; + +typedef enum { + MEMCACHED_BEHAVIOR_NO_BLOCK, + MEMCACHED_BEHAVIOR_TCP_NODELAY, + MEMCACHED_BEHAVIOR_HASH, + MEMCACHED_BEHAVIOR_KETAMA, + MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE, + MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE, + MEMCACHED_BEHAVIOR_CACHE_LOOKUPS, + MEMCACHED_BEHAVIOR_SUPPORT_CAS, + MEMCACHED_BEHAVIOR_POLL_TIMEOUT, + MEMCACHED_BEHAVIOR_DISTRIBUTION, + MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, + MEMCACHED_BEHAVIOR_USER_DATA, + MEMCACHED_BEHAVIOR_SORT_HOSTS, + MEMCACHED_BEHAVIOR_VERIFY_KEY, + MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, + MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, + MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, + MEMCACHED_BEHAVIOR_KETAMA_HASH, + MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, + MEMCACHED_BEHAVIOR_SND_TIMEOUT, + MEMCACHED_BEHAVIOR_RCV_TIMEOUT, + MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, + MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK, + MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK, + MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH, + MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY, + MEMCACHED_BEHAVIOR_NOREPLY, + MEMCACHED_BEHAVIOR_USE_UDP, + MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS, + MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, + MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE, + MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ +} memcached_behavior_t; + +#define MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED 0 +#define MEMCACHED_KETAMA_COMPAT_SPY 1 + +typedef enum { + MEMCACHED_CALLBACK_PREFIX_KEY = 0, + MEMCACHED_CALLBACK_USER_DATA = 1, + MEMCACHED_CALLBACK_CLEANUP_FUNCTION = 2, + MEMCACHED_CALLBACK_CLONE_FUNCTION = 3, +#ifdef MEMCACHED_ENABLE_DEPRECATED + MEMCACHED_CALLBACK_MALLOC_FUNCTION = 4, + MEMCACHED_CALLBACK_REALLOC_FUNCTION = 5, + MEMCACHED_CALLBACK_FREE_FUNCTION = 6, +#endif + MEMCACHED_CALLBACK_GET_FAILURE = 7, + MEMCACHED_CALLBACK_DELETE_TRIGGER = 8 +} memcached_callback_t; + +typedef enum { + MEMCACHED_HASH_DEFAULT= 0, + MEMCACHED_HASH_MD5, + MEMCACHED_HASH_CRC, + MEMCACHED_HASH_FNV1_64, + MEMCACHED_HASH_FNV1A_64, + MEMCACHED_HASH_FNV1_32, + MEMCACHED_HASH_FNV1A_32, + MEMCACHED_HASH_HSIEH, + MEMCACHED_HASH_MURMUR, + MEMCACHED_HASH_JENKINS +} memcached_hash_t; + +typedef enum { + MEMCACHED_CONNECTION_UNKNOWN, + MEMCACHED_CONNECTION_TCP, + MEMCACHED_CONNECTION_UDP, + MEMCACHED_CONNECTION_UNIX_SOCKET +} memcached_connection_t; + +#endif /* __MEMCACHED_CONSTANTS_H__ */ diff --git a/libmemcached/delete.c b/libmemcached/delete.c new file mode 100644 index 00000000..bbdd2527 --- /dev/null +++ b/libmemcached/delete.c @@ -0,0 +1,185 @@ +#include "common.h" +#include "memcached/protocol_binary.h" + +memcached_return_t memcached_delete(memcached_st *ptr, const char *key, size_t key_length, + time_t expiration) +{ + return memcached_delete_by_key(ptr, key, key_length, + key, key_length, expiration); +} + +static inline memcached_return_t binary_delete(memcached_st *ptr, + unsigned int server_key, + const char *key, + size_t key_length, + uint8_t flush); + +memcached_return_t memcached_delete_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + time_t expiration) +{ + uint8_t to_write; + size_t send_length; + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + unsigned int server_key; + + LIBMEMCACHED_MEMCACHED_DELETE_START(); + + rc= memcached_validate_key_length(key_length, + ptr->flags.binary_protocol); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + unlikely (ptr->hosts == NULL || ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + server_key= memcached_generate_hash(ptr, master_key, master_key_length); + to_write= (uint8_t)((ptr->flags.buffer_requests) ? 0 : 1); + bool no_reply= (ptr->flags.no_reply); + + if (ptr->flags.binary_protocol) + { + likely (!expiration) + rc= binary_delete(ptr, server_key, key, key_length, to_write); + else + rc= MEMCACHED_INVALID_ARGUMENTS; + } + else + { + unlikely (expiration) + { + if ((ptr->hosts[server_key].major_version == 1 && + ptr->hosts[server_key].minor_version > 2) || + ptr->hosts[server_key].major_version > 1) + { + rc= MEMCACHED_INVALID_ARGUMENTS; + goto error; + } + else + { + if (ptr->hosts[server_key].minor_version == 0) + { + if (no_reply || !to_write) + { + /* We might get out of sync with the server if we + * send this command to a server newer than 1.2.x.. + * disable no_reply and buffered mode. + */ + to_write= 1; + if (no_reply) + memcached_server_response_increment(&ptr->hosts[server_key]); + no_reply= false; + } + } + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "delete %s%.*s %u%s\r\n", + ptr->prefix_key, + (int) key_length, key, + (uint32_t)expiration, + no_reply ? " noreply" :"" ); + } + } + else + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "delete %s%.*s%s\r\n", + ptr->prefix_key, + (int)key_length, key, no_reply ? " noreply" :""); + + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) + { + rc= MEMCACHED_WRITE_FAILURE; + goto error; + } + + if (ptr->flags.use_udp && !to_write) + { + if (send_length > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + return MEMCACHED_WRITE_FAILURE; + if (send_length + ptr->hosts[server_key].write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + memcached_io_write(&ptr->hosts[server_key], NULL, 0, 1); + } + + rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, to_write); + } + + if (rc != MEMCACHED_SUCCESS) + goto error; + + if (!to_write) + rc= MEMCACHED_BUFFERED; + else if (!no_reply) + { + rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + if (rc == MEMCACHED_DELETED) + rc= MEMCACHED_SUCCESS; + } + + if (rc == MEMCACHED_SUCCESS && ptr->delete_trigger) + ptr->delete_trigger(ptr, key, key_length); + +error: + LIBMEMCACHED_MEMCACHED_DELETE_END(); + return rc; +} + +static inline memcached_return_t binary_delete(memcached_st *ptr, + unsigned int server_key, + const char *key, + size_t key_length, + uint8_t flush) +{ + protocol_binary_request_delete request= {.bytes= {0}}; + + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + if (ptr->flags.no_reply) + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; + else + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETE; + request.message.header.request.keylen= htons((uint16_t)key_length); + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl((uint32_t) key_length); + + if (ptr->flags.use_udp && !flush) + { + size_t cmd_size= sizeof(request.bytes) + key_length; + if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + return MEMCACHED_WRITE_FAILURE; + if (cmd_size + ptr->hosts[server_key].write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + memcached_io_write(&ptr->hosts[server_key], NULL, 0, 1); + } + + memcached_return_t rc= MEMCACHED_SUCCESS; + + if ((memcached_do(&ptr->hosts[server_key], request.bytes, + sizeof(request.bytes), 0) != MEMCACHED_SUCCESS) || + (memcached_io_write(&ptr->hosts[server_key], key, + key_length, (char) flush) == -1)) + { + memcached_io_reset(&ptr->hosts[server_key]); + rc= MEMCACHED_WRITE_FAILURE; + } + + unlikely (ptr->number_of_replicas > 0) + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; + + for (uint32_t x= 0; x < ptr->number_of_replicas; ++x) + { + ++server_key; + if (server_key == ptr->number_of_hosts) + server_key= 0; + + memcached_server_st* server= &ptr->hosts[server_key]; + if ((memcached_do(server, (const char*)request.bytes, + sizeof(request.bytes), 0) != MEMCACHED_SUCCESS) || + (memcached_io_write(server, key, key_length, (char) flush) == -1)) + memcached_io_reset(server); + else + memcached_server_response_decrement(server); + } + } + + return rc; +} diff --git a/libmemcached/do.c b/libmemcached/do.c new file mode 100644 index 00000000..d673f187 --- /dev/null +++ b/libmemcached/do.c @@ -0,0 +1,34 @@ +#include "common.h" + +memcached_return_t memcached_do(memcached_server_st *ptr, const void *command, + size_t command_length, uint8_t with_flush) +{ + memcached_return_t rc; + ssize_t sent_length; + + WATCHPOINT_ASSERT(command_length); + WATCHPOINT_ASSERT(command); + + if ((rc= memcached_connect(ptr)) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(rc); + return rc; + } + + /* + ** Since non buffering ops in UDP mode dont check to make sure they will fit + ** before they start writing, if there is any data in buffer, clear it out, + ** otherwise we might get a partial write. + **/ + if (ptr->type == MEMCACHED_CONNECTION_UDP && with_flush && ptr->write_buffer_offset > UDP_DATAGRAM_HEADER_LENGTH) + memcached_io_write(ptr, NULL, 0, 1); + + sent_length= memcached_io_write(ptr, command, command_length, (char) with_flush); + + if (sent_length == -1 || (size_t)sent_length != command_length) + rc= MEMCACHED_WRITE_FAILURE; + else if ((ptr->root->flags.no_reply) == 0) + memcached_server_response_increment(ptr); + + return rc; +} diff --git a/libmemcached/dump.c b/libmemcached/dump.c new file mode 100644 index 00000000..4c1006e7 --- /dev/null +++ b/libmemcached/dump.c @@ -0,0 +1,91 @@ +/* + We use this to dump all keys. + + At this point we only support a callback method. This could be optimized by first + calling items and finding active slabs. For the moment though we just loop through + all slabs on servers and "grab" the keys. +*/ + +#include "common.h" +static memcached_return_t ascii_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) +{ + memcached_return_t rc= 0; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + size_t send_length; + uint32_t server_key; + uint32_t x; + + unlikely (ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + for (server_key= 0; server_key < ptr->number_of_hosts; server_key++) + { + /* 256 I BELIEVE is the upper limit of slabs */ + for (x= 0; x < 256; x++) + { + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "stats cachedump %u 0 0\r\n", x); + + rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, 1); + + unlikely (rc != MEMCACHED_SUCCESS) + goto error; + + while (1) + { + uint32_t callback_counter; + rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + if (rc == MEMCACHED_ITEM) + { + char *string_ptr, *end_ptr; + char *key; + + string_ptr= buffer; + string_ptr+= 5; /* Move past ITEM */ + for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); + key= string_ptr; + key[(size_t)(end_ptr-string_ptr)]= 0; + for (callback_counter= 0; callback_counter < number_of_callbacks; callback_counter++) + { + rc= (*callback[callback_counter])(ptr, key, (size_t)(end_ptr-string_ptr), context); + if (rc != MEMCACHED_SUCCESS) + break; + } + } + else if (rc == MEMCACHED_END) + break; + else if (rc == MEMCACHED_SERVER_ERROR || rc == MEMCACHED_CLIENT_ERROR) + { + /* If we try to request stats cachedump for a slab class that is too big + * the server will return an incorrect error message: + * "MEMCACHED_SERVER_ERROR failed to allocate memory" + * This isn't really a fatal error, so let's just skip it. I want to + * fix the return value from the memcached server to a CLIENT_ERROR, + * so let's add support for that as well right now. + */ + rc= MEMCACHED_END; + break; + } + else + goto error; + } + } + } + +error: + if (rc == MEMCACHED_END) + return MEMCACHED_SUCCESS; + else + return rc; +} + +memcached_return_t memcached_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) +{ + /* No support for Binary protocol yet */ + if (ptr->flags.binary_protocol) + return MEMCACHED_FAILURE; + + return ascii_dump(ptr, callback, context, number_of_callbacks); +} + diff --git a/libmemcached/fetch.c b/libmemcached/fetch.c new file mode 100644 index 00000000..7baac544 --- /dev/null +++ b/libmemcached/fetch.c @@ -0,0 +1,105 @@ +#include "common.h" + +char *memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, + size_t *value_length, + uint32_t *flags, + memcached_return_t *error) +{ + memcached_result_st *result_buffer= &ptr->result; + + unlikely (ptr->flags.use_udp) + { + *error= MEMCACHED_NOT_SUPPORTED; + return NULL; + } + + result_buffer= memcached_fetch_result(ptr, result_buffer, error); + + if (result_buffer == NULL || *error != MEMCACHED_SUCCESS) + { + WATCHPOINT_ASSERT(result_buffer == NULL); + *value_length= 0; + return NULL; + } + + *value_length= memcached_string_length(&result_buffer->value); + + if (key) + { + strncpy(key, result_buffer->key, result_buffer->key_length); + *key_length= result_buffer->key_length; + } + + if (result_buffer->flags) + *flags= result_buffer->flags; + else + *flags= 0; + + return memcached_string_c_copy(&result_buffer->value); +} + +memcached_result_st *memcached_fetch_result(memcached_st *ptr, + memcached_result_st *result, + memcached_return_t *error) +{ + memcached_server_st *server; + + unlikely (ptr->flags.use_udp) + { + *error= MEMCACHED_NOT_SUPPORTED; + return NULL; + } + + if (result == NULL) + if ((result= memcached_result_create(ptr, NULL)) == NULL) + return NULL; + + while ((server = memcached_io_get_readable_server(ptr)) != NULL) + { + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + *error= memcached_response(server, buffer, sizeof(buffer), result); + + if (*error == MEMCACHED_SUCCESS) + return result; + else if (*error == MEMCACHED_END) + memcached_server_response_reset(server); + else if (*error != MEMCACHED_NOTFOUND) + break; + } + + /* We have completed reading data */ + if (memcached_is_allocated(result)) + { + memcached_result_free(result); + } + else + { + memcached_string_reset(&result->value); + } + + return NULL; +} + +memcached_return_t memcached_fetch_execute(memcached_st *ptr, + memcached_execute_fn *callback, + void *context, + uint32_t number_of_callbacks) +{ + memcached_result_st *result= &ptr->result; + memcached_return_t rc= MEMCACHED_FAILURE; + unsigned int x; + + while ((result= memcached_fetch_result(ptr, result, &rc)) != NULL) + { + if (rc == MEMCACHED_SUCCESS) + { + for (x= 0; x < number_of_callbacks; x++) + { + rc= (*callback[x])(ptr, result, context); + if (rc != MEMCACHED_SUCCESS) + break; + } + } + } + return rc; +} diff --git a/libmemcached/flush.c b/libmemcached/flush.c new file mode 100644 index 00000000..d05d7442 --- /dev/null +++ b/libmemcached/flush.c @@ -0,0 +1,90 @@ +#include "common.h" + +static memcached_return_t memcached_flush_binary(memcached_st *ptr, + time_t expiration); +static memcached_return_t memcached_flush_textual(memcached_st *ptr, + time_t expiration); + +memcached_return_t memcached_flush(memcached_st *ptr, time_t expiration) +{ + memcached_return_t rc; + + LIBMEMCACHED_MEMCACHED_FLUSH_START(); + if (ptr->flags.binary_protocol) + rc= memcached_flush_binary(ptr, expiration); + else + rc= memcached_flush_textual(ptr, expiration); + LIBMEMCACHED_MEMCACHED_FLUSH_END(); + return rc; +} + +static memcached_return_t memcached_flush_textual(memcached_st *ptr, + time_t expiration) +{ + unsigned int x; + size_t send_length; + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + unlikely (ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + for (x= 0; x < ptr->number_of_hosts; x++) + { + bool no_reply= ptr->flags.no_reply; + + if (expiration) + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "flush_all %llu%s\r\n", + (unsigned long long)expiration, no_reply ? " noreply" : ""); + else + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "flush_all%s\r\n", no_reply ? " noreply" : ""); + + rc= memcached_do(&ptr->hosts[x], buffer, send_length, 1); + + if (rc == MEMCACHED_SUCCESS && !no_reply) + (void)memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + } + + return MEMCACHED_SUCCESS; +} + +static memcached_return_t memcached_flush_binary(memcached_st *ptr, + time_t expiration) +{ + unsigned int x; + protocol_binary_request_flush request= {.bytes= {0}}; + + unlikely (ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + request.message.header.request.magic= (uint8_t)PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; + request.message.header.request.extlen= 4; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl(request.message.header.request.extlen); + request.message.body.expiration= htonl((uint32_t) expiration); + + for (x= 0; x < ptr->number_of_hosts; x++) + { + if (ptr->flags.no_reply) + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSHQ; + else + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; + if (memcached_do(&ptr->hosts[x], request.bytes, + sizeof(request.bytes), 1) != MEMCACHED_SUCCESS) + { + memcached_io_reset(&ptr->hosts[x]); + return MEMCACHED_WRITE_FAILURE; + } + } + + for (x= 0; x < ptr->number_of_hosts; x++) + { + if (memcached_server_response_count(&ptr->hosts[x]) > 0) + (void)memcached_response(&ptr->hosts[x], NULL, 0, NULL); + } + + return MEMCACHED_SUCCESS; +} diff --git a/libmemcached/flush_buffers.c b/libmemcached/flush_buffers.c new file mode 100644 index 00000000..950fa1e8 --- /dev/null +++ b/libmemcached/flush_buffers.c @@ -0,0 +1,21 @@ +#include "common.h" + +memcached_return_t memcached_flush_buffers(memcached_st *mem) +{ + memcached_return_t ret= MEMCACHED_SUCCESS; + + for (uint32_t x= 0; x < mem->number_of_hosts; ++x) + if (mem->hosts[x].write_buffer_offset != 0) + { + if (mem->hosts[x].fd == -1 && + (ret= memcached_connect(&mem->hosts[x])) != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(ret); + return ret; + } + if (memcached_io_write(&mem->hosts[x], NULL, 0, 1) == -1) + ret= MEMCACHED_SOME_ERRORS; + } + + return ret; +} diff --git a/libmemcached/get.c b/libmemcached/get.c new file mode 100644 index 00000000..30682427 --- /dev/null +++ b/libmemcached/get.c @@ -0,0 +1,574 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Get functions for libmemcached + * + */ + +#include "common.h" + +/* + What happens if no servers exist? +*/ +char *memcached_get(memcached_st *ptr, const char *key, + size_t key_length, + size_t *value_length, + uint32_t *flags, + memcached_return_t *error) +{ + return memcached_get_by_key(ptr, NULL, 0, key, key_length, value_length, + flags, error); +} + +static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode); + +char *memcached_get_by_key(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char *key, size_t key_length, + size_t *value_length, + uint32_t *flags, + memcached_return_t *error) +{ + char *value; + size_t dummy_length; + uint32_t dummy_flags; + memcached_return_t dummy_error; + + unlikely (ptr->flags.use_udp) + { + *error= MEMCACHED_NOT_SUPPORTED; + return NULL; + } + + /* Request the key */ + *error= memcached_mget_by_key_real(ptr, master_key, master_key_length, + (const char * const *)&key, + &key_length, 1, false); + + value= memcached_fetch(ptr, NULL, NULL, + value_length, flags, error); + /* This is for historical reasons */ + if (*error == MEMCACHED_END) + *error= MEMCACHED_NOTFOUND; + + if (value == NULL) + { + if (ptr->get_key_failure && *error == MEMCACHED_NOTFOUND) + { + memcached_return_t rc; + + memcached_result_reset(&ptr->result); + rc= ptr->get_key_failure(ptr, key, key_length, &ptr->result); + + /* On all failure drop to returning NULL */ + if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) + { + if (rc == MEMCACHED_BUFFERED) + { + uint64_t latch; /* We use latch to track the state of the original socket */ + latch= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS); + if (latch == 0) + memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1); + + rc= memcached_set(ptr, key, key_length, + memcached_result_value(&ptr->result), + memcached_result_length(&ptr->result), + 0, memcached_result_flags(&ptr->result)); + + if (rc == MEMCACHED_BUFFERED && latch == 0) + memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0); + } + else + { + rc= memcached_set(ptr, key, key_length, + memcached_result_value(&ptr->result), + memcached_result_length(&ptr->result), + 0, memcached_result_flags(&ptr->result)); + } + + if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) + { + *error= rc; + *value_length= memcached_result_length(&ptr->result); + *flags= memcached_result_flags(&ptr->result); + return memcached_string_c_copy(&ptr->result.value); + } + } + } + + return NULL; + } + + (void)memcached_fetch(ptr, NULL, NULL, + &dummy_length, &dummy_flags, + &dummy_error); + WATCHPOINT_ASSERT(dummy_length == 0); + + return value; +} + +memcached_return_t memcached_mget(memcached_st *ptr, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + return memcached_mget_by_key(ptr, NULL, 0, keys, key_length, number_of_keys); +} + +static memcached_return_t binary_mget_by_key(memcached_st *ptr, + unsigned int master_server_key, + bool is_master_key_set, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode); + +static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode) +{ + unsigned int x; + memcached_return_t rc= MEMCACHED_NOTFOUND; + const char *get_command= "get "; + uint8_t get_command_length= 4; + unsigned int master_server_key= (unsigned int)-1; /* 0 is a valid server id! */ + bool is_master_key_set= false; + + unlikely (ptr->flags.use_udp) + return MEMCACHED_NOT_SUPPORTED; + + LIBMEMCACHED_MEMCACHED_MGET_START(); + ptr->cursor_server= 0; + + if (number_of_keys == 0) + return MEMCACHED_NOTFOUND; + + if (ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + if (ptr->flags.verify_key && (memcached_key_test(keys, key_length, number_of_keys) == MEMCACHED_BAD_KEY_PROVIDED)) + return MEMCACHED_BAD_KEY_PROVIDED; + + if (master_key && master_key_length) + { + if (ptr->flags.verify_key && (memcached_key_test((const char * const *)&master_key, &master_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + return MEMCACHED_BAD_KEY_PROVIDED; + master_server_key= memcached_generate_hash(ptr, master_key, master_key_length); + is_master_key_set= true; + } + + /* + Here is where we pay for the non-block API. We need to remove any data sitting + in the queue before we start our get. + + It might be optimum to bounce the connection if count > some number. + */ + for (x= 0; x < ptr->number_of_hosts; x++) + { + if (memcached_server_response_count(&ptr->hosts[x])) + { + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + if (ptr->flags.no_block) + (void)memcached_io_write(&ptr->hosts[x], NULL, 0, 1); + + while(memcached_server_response_count(&ptr->hosts[x])) + (void)memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, &ptr->result); + } + } + + if (ptr->flags.binary_protocol) + return binary_mget_by_key(ptr, master_server_key, is_master_key_set, keys, + key_length, number_of_keys, mget_mode); + + if (ptr->flags.support_cas) + { + get_command= "gets "; + get_command_length= 5; + } + + /* + If a server fails we warn about errors and start all over with sending keys + to the server. + */ + for (x= 0; x < number_of_keys; x++) + { + unsigned int server_key; + + if (is_master_key_set) + server_key= master_server_key; + else + server_key= memcached_generate_hash(ptr, keys[x], key_length[x]); + + if (memcached_server_response_count(&ptr->hosts[server_key]) == 0) + { + rc= memcached_connect(&ptr->hosts[server_key]); + + if (rc != MEMCACHED_SUCCESS) + continue; + + if ((memcached_io_write(&ptr->hosts[server_key], get_command, get_command_length, 0)) == -1) + { + rc= MEMCACHED_SOME_ERRORS; + continue; + } + WATCHPOINT_ASSERT(ptr->hosts[server_key].cursor_active == 0); + memcached_server_response_increment(&ptr->hosts[server_key]); + WATCHPOINT_ASSERT(ptr->hosts[server_key].cursor_active == 1); + } + + /* Only called when we have a prefix key */ + if (ptr->prefix_key[0] != 0) + { + if ((memcached_io_write(&ptr->hosts[server_key], ptr->prefix_key, ptr->prefix_key_length, 0)) == -1) + { + memcached_server_response_reset(&ptr->hosts[server_key]); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + } + + if ((memcached_io_write(&ptr->hosts[server_key], keys[x], key_length[x], 0)) == -1) + { + memcached_server_response_reset(&ptr->hosts[server_key]); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + if ((memcached_io_write(&ptr->hosts[server_key], " ", 1, 0)) == -1) + { + memcached_server_response_reset(&ptr->hosts[server_key]); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + } + + /* + Should we muddle on if some servers are dead? + */ + for (x= 0; x < ptr->number_of_hosts; x++) + { + if (memcached_server_response_count(&ptr->hosts[x])) + { + /* We need to do something about non-connnected hosts in the future */ + if ((memcached_io_write(&ptr->hosts[x], "\r\n", 2, 1)) == -1) + { + rc= MEMCACHED_SOME_ERRORS; + } + } + } + + LIBMEMCACHED_MEMCACHED_MGET_END(); + return rc; +} + +memcached_return_t memcached_mget_by_key(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + return memcached_mget_by_key_real(ptr, master_key, master_key_length, keys, + key_length, number_of_keys, true); +} + +memcached_return_t memcached_mget_execute(memcached_st *ptr, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + memcached_execute_fn *callback, + void *context, + unsigned int number_of_callbacks) +{ + return memcached_mget_execute_by_key(ptr, NULL, 0, keys, key_length, + number_of_keys, callback, + context, number_of_callbacks); +} + +memcached_return_t memcached_mget_execute_by_key(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + memcached_execute_fn *callback, + void *context, + unsigned int number_of_callbacks) +{ + if ((ptr->flags.binary_protocol) == 0) + return MEMCACHED_NOT_SUPPORTED; + + memcached_return_t rc; + memcached_callback_st *original_callbacks= ptr->callbacks; + memcached_callback_st cb= { + .callback= callback, + .context= context, + .number_of_callback= number_of_callbacks + }; + + ptr->callbacks= &cb; + rc= memcached_mget_by_key(ptr, master_key, master_key_length, keys, + key_length, number_of_keys); + ptr->callbacks= original_callbacks; + return rc; +} + +static memcached_return_t simple_binary_mget(memcached_st *ptr, + unsigned int master_server_key, + bool is_master_key_set, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, bool mget_mode) +{ + memcached_return_t rc= MEMCACHED_NOTFOUND; + uint32_t x; + + int flush= number_of_keys == 1; + + /* + If a server fails we warn about errors and start all over with sending keys + to the server. + */ + for (x= 0; x < number_of_keys; x++) + { + unsigned int server_key; + + if (is_master_key_set) + server_key= master_server_key; + else + server_key= memcached_generate_hash(ptr, keys[x], key_length[x]); + + if (memcached_server_response_count(&ptr->hosts[server_key]) == 0) + { + rc= memcached_connect(&ptr->hosts[server_key]); + if (rc != MEMCACHED_SUCCESS) + continue; + } + + protocol_binary_request_getk request= {.bytes= {0}}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + if (mget_mode) + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ; + else + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK; + + memcached_return_t vk; + vk= memcached_validate_key_length(key_length[x], + ptr->flags.binary_protocol); + unlikely (vk != MEMCACHED_SUCCESS) + { + if (x > 0) + memcached_io_reset(&ptr->hosts[server_key]); + return vk; + } + + request.message.header.request.keylen= htons((uint16_t)key_length[x]); + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + request.message.header.request.bodylen= htonl((uint32_t) key_length[x]); + + if ((memcached_io_write(&ptr->hosts[server_key], request.bytes, + sizeof(request.bytes), 0) == -1) || + (memcached_io_write(&ptr->hosts[server_key], keys[x], + key_length[x], (char) flush) == -1)) + { + memcached_server_response_reset(&ptr->hosts[server_key]); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + /* We just want one pending response per server */ + memcached_server_response_reset(&ptr->hosts[server_key]); + memcached_server_response_increment(&ptr->hosts[server_key]); + if ((x > 0 && x == ptr->io_key_prefetch) && + memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS) + rc= MEMCACHED_SOME_ERRORS; + } + + if (mget_mode) + { + /* + * Send a noop command to flush the buffers + */ + protocol_binary_request_noop request= {.bytes= {0}}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_NOOP; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + + for (x= 0; x < ptr->number_of_hosts; x++) + if (memcached_server_response_count(&ptr->hosts[x])) + { + if (memcached_io_write(&ptr->hosts[x], NULL, 0, 1) == -1) + { + memcached_server_response_reset(&ptr->hosts[x]); + memcached_io_reset(&ptr->hosts[x]); + rc= MEMCACHED_SOME_ERRORS; + } + + if (memcached_io_write(&ptr->hosts[x], request.bytes, + sizeof(request.bytes), 1) == -1) + { + memcached_server_response_reset(&ptr->hosts[x]); + memcached_io_reset(&ptr->hosts[x]); + rc= MEMCACHED_SOME_ERRORS; + } + } + } + + + return rc; +} + +static memcached_return_t replication_binary_mget(memcached_st *ptr, + uint32_t* hash, + bool* dead_servers, + const char *const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + memcached_return_t rc= MEMCACHED_NOTFOUND; + uint32_t x, start= 0; + uint64_t randomize_read= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ); + + if (randomize_read) + start= (uint32_t)random() % (uint32_t)(ptr->number_of_replicas + 1); + + /* Loop for each replica */ + for (uint32_t replica= 0; replica <= ptr->number_of_replicas; ++replica) + { + bool success= true; + + for (x= 0; x < number_of_keys; ++x) + { + if (hash[x] == ptr->number_of_hosts) + continue; /* Already successfully sent */ + + uint32_t server= hash[x] + replica; + + /* In case of randomized reads */ + if (randomize_read && ((server + start) <= (hash[x] + ptr->number_of_replicas))) + server += start; + + while (server >= ptr->number_of_hosts) + server -= ptr->number_of_hosts; + + if (dead_servers[server]) + continue; + + if (memcached_server_response_count(&ptr->hosts[server]) == 0) + { + rc= memcached_connect(&ptr->hosts[server]); + if (rc != MEMCACHED_SUCCESS) + { + memcached_io_reset(&ptr->hosts[server]); + dead_servers[server]= true; + success= false; + continue; + } + } + + protocol_binary_request_getk request= { + .message.header.request= { + .magic= PROTOCOL_BINARY_REQ, + .opcode= PROTOCOL_BINARY_CMD_GETK, + .keylen= htons((uint16_t)key_length[x]), + .datatype= PROTOCOL_BINARY_RAW_BYTES, + .bodylen= htonl((uint32_t)key_length[x]) + } + }; + + /* + * We need to disable buffering to actually know that the request was + * successfully sent to the server (so that we should expect a result + * back). It would be nice to do this in buffered mode, but then it + * would be complex to handle all error situations if we got to send + * some of the messages, and then we failed on writing out some others + * and we used the callback interface from memcached_mget_execute so + * that we might have processed some of the responses etc. For now, + * just make sure we work _correctly_ + */ + if ((memcached_io_write(&ptr->hosts[server], request.bytes, + sizeof(request.bytes), 0) == -1) || + (memcached_io_write(&ptr->hosts[server], keys[x], + key_length[x], 1) == -1)) + { + memcached_io_reset(&ptr->hosts[server]); + dead_servers[server]= true; + success= false; + continue; + } + + memcached_server_response_increment(&ptr->hosts[server]); + hash[x]= ptr->number_of_hosts; + } + + if (success) + break; + } + + return rc; +} + +static memcached_return_t binary_mget_by_key(memcached_st *ptr, + unsigned int master_server_key, + bool is_master_key_set, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + bool mget_mode) +{ + memcached_return_t rc; + + if (ptr->number_of_replicas == 0) + { + rc= simple_binary_mget(ptr, master_server_key, is_master_key_set, + keys, key_length, number_of_keys, mget_mode); + } + else + { + uint32_t* hash; + bool* dead_servers; + + hash= ptr->call_malloc(ptr, sizeof(uint32_t) * number_of_keys); + dead_servers= ptr->call_calloc(ptr, ptr->number_of_hosts, sizeof(bool)); + + if (hash == NULL || dead_servers == NULL) + { + ptr->call_free(ptr, hash); + ptr->call_free(ptr, dead_servers); + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + } + + if (is_master_key_set) + for (unsigned int x= 0; x < number_of_keys; x++) + hash[x]= master_server_key; + else + for (unsigned int x= 0; x < number_of_keys; x++) + hash[x]= memcached_generate_hash(ptr, keys[x], key_length[x]); + + rc= replication_binary_mget(ptr, hash, dead_servers, keys, + key_length, number_of_keys); + + ptr->call_free(ptr, hash); + ptr->call_free(ptr, dead_servers); + + return MEMCACHED_SUCCESS; + } + + return rc; +} diff --git a/libmemcached/get.h b/libmemcached/get.h new file mode 100644 index 00000000..bb04a154 --- /dev/null +++ b/libmemcached/get.h @@ -0,0 +1,84 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Get functions for libmemcached + * + */ + +#ifndef LIBMEMCACHED_MEMCACHED_GET_H +#define LIBMEMCACHED_MEMCACHED_GET_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Public defines */ +LIBMEMCACHED_API +char *memcached_get(memcached_st *ptr, + const char *key, size_t key_length, + size_t *value_length, + uint32_t *flags, + memcached_return_t *error); + +LIBMEMCACHED_API +memcached_return_t memcached_mget(memcached_st *ptr, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys); + +LIBMEMCACHED_API +char *memcached_get_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + size_t *value_length, + uint32_t *flags, + memcached_return_t *error); + +LIBMEMCACHED_API +memcached_return_t memcached_mget_by_key(memcached_st *ptr, + const char *master_key, size_t + master_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys); + +LIBMEMCACHED_API +char *memcached_fetch(memcached_st *ptr, + char *key, size_t *key_length, + size_t *value_length, uint32_t *flags, + memcached_return_t *error); + +LIBMEMCACHED_API +memcached_result_st *memcached_fetch_result(memcached_st *ptr, + memcached_result_st *result, + memcached_return_t *error); + +LIBMEMCACHED_API +memcached_return_t memcached_mget_execute(memcached_st *ptr, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + memcached_execute_fn *callback, + void *context, + unsigned int number_of_callbacks); + +LIBMEMCACHED_API +memcached_return_t memcached_mget_execute_by_key(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char * const *keys, + const size_t *key_length, + size_t number_of_keys, + memcached_execute_fn *callback, + void *context, + unsigned int number_of_callbacks); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBMEMCACHED_MEMCACHED_GET_H */ diff --git a/libmemcached/hash.c b/libmemcached/hash.c new file mode 100644 index 00000000..395020a3 --- /dev/null +++ b/libmemcached/hash.c @@ -0,0 +1,234 @@ +#include "common.h" + + +/* Defines */ +static uint64_t FNV_64_INIT= UINT64_C(0xcbf29ce484222325); +static uint64_t FNV_64_PRIME= UINT64_C(0x100000001b3); + +static uint32_t FNV_32_INIT= 2166136261UL; +static uint32_t FNV_32_PRIME= 16777619; + +/* Prototypes */ +static uint32_t internal_generate_hash(const char *key, size_t key_length); +static uint32_t internal_generate_md5(const char *key, size_t key_length); + +uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memcached_hash_t hash_algorithm) +{ + uint32_t hash= 1; /* Just here to remove compile warning */ + uint32_t x= 0; + + switch (hash_algorithm) + { + case MEMCACHED_HASH_DEFAULT: + hash= internal_generate_hash(key, key_length); + break; + case MEMCACHED_HASH_MD5: + hash= internal_generate_md5(key, key_length); + break; + case MEMCACHED_HASH_CRC: + hash= ((hash_crc32(key, key_length) >> 16) & 0x7fff); + if (hash == 0) + hash= 1; + break; + /* FNV hash'es lifted from Dustin Sallings work */ + case MEMCACHED_HASH_FNV1_64: + { + /* Thanks to pierre@demartines.com for the pointer */ + uint64_t temp_hash; + + temp_hash= FNV_64_INIT; + for (x= 0; x < key_length; x++) + { + temp_hash *= FNV_64_PRIME; + temp_hash ^= (uint64_t)key[x]; + } + hash= (uint32_t)temp_hash; + } + break; + case MEMCACHED_HASH_FNV1A_64: + { + hash= (uint32_t) FNV_64_INIT; + for (x= 0; x < key_length; x++) + { + uint32_t val= (uint32_t)key[x]; + hash ^= val; + hash *= (uint32_t) FNV_64_PRIME; + } + } + break; + case MEMCACHED_HASH_FNV1_32: + { + hash= FNV_32_INIT; + for (x= 0; x < key_length; x++) + { + uint32_t val= (uint32_t)key[x]; + hash *= FNV_32_PRIME; + hash ^= val; + } + } + break; + case MEMCACHED_HASH_FNV1A_32: + { + hash= FNV_32_INIT; + for (x= 0; x < key_length; x++) + { + uint32_t val= (uint32_t)key[x]; + hash ^= val; + hash *= FNV_32_PRIME; + } + } + break; + case MEMCACHED_HASH_HSIEH: + { +#ifdef HAVE_HSIEH_HASH + hash= hsieh_hash(key, key_length); +#endif + break; + } + case MEMCACHED_HASH_MURMUR: + { + hash= murmur_hash(key, key_length); + break; + } + case MEMCACHED_HASH_JENKINS: + { + hash=jenkins_hash(key, key_length, 13); + break; + } + default: + { + WATCHPOINT_ASSERT(hash_algorithm); + break; + } + } + return hash; +} + +uint32_t generate_hash(memcached_st *ptr, const char *key, size_t key_length) +{ + uint32_t hash= 1; /* Just here to remove compile warning */ + + + WATCHPOINT_ASSERT(ptr->number_of_hosts); + + if (ptr->number_of_hosts == 1) + return 0; + + hash= memcached_generate_hash_value(key, key_length, ptr->hash); + WATCHPOINT_ASSERT(hash); + return hash; +} + +static uint32_t dispatch_host(memcached_st *ptr, uint32_t hash) +{ + switch (ptr->distribution) + { + case MEMCACHED_DISTRIBUTION_CONSISTENT: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: + { + uint32_t num= ptr->continuum_points_counter; + WATCHPOINT_ASSERT(ptr->continuum); + + hash= hash; + memcached_continuum_item_st *begin, *end, *left, *right, *middle; + begin= left= ptr->continuum; + end= right= ptr->continuum + num; + + while (left < right) + { + middle= left + (right - left) / 2; + if (middle->value < hash) + left= middle + 1; + else + right= middle; + } + if (right == end) + right= begin; + return right->index; + } + case MEMCACHED_DISTRIBUTION_MODULA: + return hash % ptr->number_of_hosts; + case MEMCACHED_DISTRIBUTION_RANDOM: + return (uint32_t) random() % ptr->number_of_hosts; + default: + WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ + return hash % ptr->number_of_hosts; + } + + /* NOTREACHED */ +} + +/* + One day make this public, and have it return the actual memcached_server_st + to the calling application. +*/ +uint32_t memcached_generate_hash(memcached_st *ptr, const char *key, size_t key_length) +{ + uint32_t hash= 1; /* Just here to remove compile warning */ + + WATCHPOINT_ASSERT(ptr->number_of_hosts); + + if (ptr->number_of_hosts == 1) + return 0; + + if (ptr->flags.hash_with_prefix_key) + { + size_t temp_length= ptr->prefix_key_length + key_length; + char temp[temp_length]; + + if (temp_length > MEMCACHED_MAX_KEY -1) + return 0; + + strncpy(temp, ptr->prefix_key, ptr->prefix_key_length); + strncpy(temp + ptr->prefix_key_length, key, key_length); + hash= generate_hash(ptr, temp, temp_length); + } + else + { + hash= generate_hash(ptr, key, key_length); + } + + WATCHPOINT_ASSERT(hash); + + if (memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS) && ptr->next_distribution_rebuild) { + struct timeval now; + + if (gettimeofday(&now, NULL) == 0 && + now.tv_sec > ptr->next_distribution_rebuild) + run_distribution(ptr); + } + + return dispatch_host(ptr, hash); +} + +static uint32_t internal_generate_hash(const char *key, size_t key_length) +{ + const char *ptr= key; + uint32_t value= 0; + + while (key_length--) + { + uint32_t val= (uint32_t) *ptr++; + value += val; + value += (value << 10); + value ^= (value >> 6); + } + value += (value << 3); + value ^= (value >> 11); + value += (value << 15); + + return value == 0 ? 1 : (uint32_t) value; +} + +static uint32_t internal_generate_md5(const char *key, size_t key_length) +{ + unsigned char results[16]; + + md5_signature((unsigned char*)key, (unsigned int)key_length, results); + + return ((uint32_t) (results[3] & 0xFF) << 24) + | ((uint32_t) (results[2] & 0xFF) << 16) + | ((uint32_t) (results[1] & 0xFF) << 8) + | (results[0] & 0xFF); +} diff --git a/libmemcached/hosts.c b/libmemcached/hosts.c new file mode 100644 index 00000000..8cc5f3e6 --- /dev/null +++ b/libmemcached/hosts.c @@ -0,0 +1,514 @@ +#include "common.h" +#include + +/* Protoypes (static) */ +static memcached_return_t server_add(memcached_st *ptr, const char *hostname, + in_port_t port, + uint32_t weight, + memcached_connection_t type); +memcached_return_t update_continuum(memcached_st *ptr); + +static int compare_servers(const void *p1, const void *p2) +{ + int return_value; + memcached_server_st *a= (memcached_server_st *)p1; + memcached_server_st *b= (memcached_server_st *)p2; + + return_value= strcmp(a->hostname, b->hostname); + + if (return_value == 0) + { + return_value= (int) (a->port - b->port); + } + + return return_value; +} + +static void sort_hosts(memcached_st *ptr) +{ + if (ptr->number_of_hosts) + { + qsort(ptr->hosts, ptr->number_of_hosts, sizeof(memcached_server_st), compare_servers); + ptr->hosts[0].count= (uint16_t) ptr->number_of_hosts; + } +} + + +memcached_return_t run_distribution(memcached_st *ptr) +{ + switch (ptr->distribution) + { + case MEMCACHED_DISTRIBUTION_CONSISTENT: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: + case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: + return update_continuum(ptr); + case MEMCACHED_DISTRIBUTION_MODULA: + if (ptr->flags.use_sort_hosts) + sort_hosts(ptr); + break; + case MEMCACHED_DISTRIBUTION_RANDOM: + break; + default: + WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ + } + + ptr->last_disconnected_server = NULL; + + return MEMCACHED_SUCCESS; +} + +void server_list_free(memcached_st *ptr, memcached_server_st *servers) +{ + unsigned int x; + + if (servers == NULL) + return; + + for (x= 0; x < servers->count; x++) + if (servers[x].address_info) + { + freeaddrinfo(servers[x].address_info); + servers[x].address_info= NULL; + } + + if (ptr) + ptr->call_free(ptr, servers); + else + free(servers); +} + +static uint32_t ketama_server_hash(const char *key, unsigned int key_length, int alignment) +{ + unsigned char results[16]; + + md5_signature((unsigned char*)key, key_length, results); + return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24) + | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16) + | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8) + | (results[0 + alignment * 4] & 0xFF); +} + +static int continuum_item_cmp(const void *t1, const void *t2) +{ + memcached_continuum_item_st *ct1= (memcached_continuum_item_st *)t1; + memcached_continuum_item_st *ct2= (memcached_continuum_item_st *)t2; + + /* Why 153? Hmmm... */ + WATCHPOINT_ASSERT(ct1->value != 153); + if (ct1->value == ct2->value) + return 0; + else if (ct1->value > ct2->value) + return 1; + else + return -1; +} + +memcached_return_t update_continuum(memcached_st *ptr) +{ + uint32_t host_index; + uint32_t continuum_index= 0; + uint32_t value; + memcached_server_st *list; + uint32_t pointer_index; + uint32_t pointer_counter= 0; + uint32_t pointer_per_server= MEMCACHED_POINTS_PER_SERVER; + uint32_t pointer_per_hash= 1; + uint64_t total_weight= 0; + uint64_t is_ketama_weighted= 0; + uint64_t is_auto_ejecting= 0; + uint32_t points_per_server= 0; + uint32_t live_servers= 0; + struct timeval now; + + if (gettimeofday(&now, NULL) != 0) + { + ptr->cached_errno = errno; + return MEMCACHED_ERRNO; + } + + list = ptr->hosts; + + /* count live servers (those without a retry delay set) */ + is_auto_ejecting= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS); + if (is_auto_ejecting) + { + live_servers= 0; + ptr->next_distribution_rebuild= 0; + for (host_index= 0; host_index < ptr->number_of_hosts; ++host_index) + { + if (list[host_index].next_retry <= now.tv_sec) + live_servers++; + else + { + if (ptr->next_distribution_rebuild == 0 || list[host_index].next_retry < ptr->next_distribution_rebuild) + ptr->next_distribution_rebuild= list[host_index].next_retry; + } + } + } + else + live_servers= ptr->number_of_hosts; + + is_ketama_weighted= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED); + points_per_server= (uint32_t) (is_ketama_weighted ? MEMCACHED_POINTS_PER_SERVER_KETAMA : MEMCACHED_POINTS_PER_SERVER); + + if (live_servers == 0) + return MEMCACHED_SUCCESS; + + if (live_servers > ptr->continuum_count) + { + memcached_continuum_item_st *new_ptr; + + new_ptr= ptr->call_realloc(ptr, ptr->continuum, + sizeof(memcached_continuum_item_st) * (live_servers + MEMCACHED_CONTINUUM_ADDITION) * points_per_server); + + if (new_ptr == 0) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + ptr->continuum= new_ptr; + ptr->continuum_count= live_servers + MEMCACHED_CONTINUUM_ADDITION; + } + + if (is_ketama_weighted) + { + for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index) + { + if (list[host_index].weight == 0) + { + list[host_index].weight = 1; + } + if (!is_auto_ejecting || list[host_index].next_retry <= now.tv_sec) + total_weight += list[host_index].weight; + } + } + + for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index) + { + if (is_auto_ejecting && list[host_index].next_retry > now.tv_sec) + continue; + + if (is_ketama_weighted) + { + float pct = (float)list[host_index].weight / (float)total_weight; + pointer_per_server= (uint32_t) ((floorf((float) (pct * MEMCACHED_POINTS_PER_SERVER_KETAMA / 4 * (float)live_servers + 0.0000000001))) * 4); + pointer_per_hash= 4; +#ifdef DEBUG + printf("ketama_weighted:%s|%d|%llu|%u\n", + list[host_index].hostname, + list[host_index].port, + (unsigned long long)list[host_index].weight, + pointer_per_server); +#endif + } + + + if (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY) + { + for (pointer_index= 0; + pointer_index < pointer_per_server / pointer_per_hash; + pointer_index++) + { + char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; + size_t sort_host_length; + + // Spymemcached ketema key format is: hostname/ip:port-index + // If hostname is not available then: /ip:port-index + sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, + "/%s:%d-%d", + list[host_index].hostname, + list[host_index].port, + pointer_index); +#ifdef DEBUG + printf("update_continuum: key is %s\n", sort_host); +#endif + + WATCHPOINT_ASSERT(sort_host_length); + + if (is_ketama_weighted) + { + unsigned int i; + for (i = 0; i < pointer_per_hash; i++) + { + value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i); + ptr->continuum[continuum_index].index= host_index; + ptr->continuum[continuum_index++].value= value; + } + } + else + { + value= memcached_generate_hash_value(sort_host, sort_host_length, ptr->hash_continuum); + ptr->continuum[continuum_index].index= host_index; + ptr->continuum[continuum_index++].value= value; + } + } + } + else + { + for (pointer_index= 1; + pointer_index <= pointer_per_server / pointer_per_hash; + pointer_index++) + { + char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; + size_t sort_host_length; + + if (list[host_index].port == MEMCACHED_DEFAULT_PORT) + { + sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, + "%s-%d", + list[host_index].hostname, + pointer_index - 1); + } + else + { + sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, + "%s:%d-%d", + list[host_index].hostname, + list[host_index].port, pointer_index - 1); + } + + WATCHPOINT_ASSERT(sort_host_length); + + if (is_ketama_weighted) + { + unsigned int i; + for (i = 0; i < pointer_per_hash; i++) + { + value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i); + ptr->continuum[continuum_index].index= host_index; + ptr->continuum[continuum_index++].value= value; + } + } + else + { + value= memcached_generate_hash_value(sort_host, sort_host_length, ptr->hash_continuum); + ptr->continuum[continuum_index].index= host_index; + ptr->continuum[continuum_index++].value= value; + } + } + } + + pointer_counter+= pointer_per_server; + } + + WATCHPOINT_ASSERT(ptr); + WATCHPOINT_ASSERT(ptr->continuum); + WATCHPOINT_ASSERT(ptr->number_of_hosts * MEMCACHED_POINTS_PER_SERVER <= MEMCACHED_CONTINUUM_SIZE); + ptr->continuum_points_counter= pointer_counter; + qsort(ptr->continuum, ptr->continuum_points_counter, sizeof(memcached_continuum_item_st), continuum_item_cmp); + +#ifdef DEBUG + for (pointer_index= 0; ptr->number_of_hosts && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++) + { + WATCHPOINT_ASSERT(ptr->continuum[pointer_index].value <= ptr->continuum[pointer_index + 1].value); + } +#endif + + return MEMCACHED_SUCCESS; +} + + +memcached_return_t memcached_server_push(memcached_st *ptr, memcached_server_st *list) +{ + unsigned int x; + uint16_t count; + memcached_server_st *new_host_list; + + if (!list) + return MEMCACHED_SUCCESS; + + count= list[0].count; + new_host_list= ptr->call_realloc(ptr, ptr->hosts, + sizeof(memcached_server_st) * (count + ptr->number_of_hosts)); + + if (!new_host_list) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + ptr->hosts= new_host_list; + + for (x= 0; x < count; x++) + { + if ((ptr->flags.use_udp && list[x].type != MEMCACHED_CONNECTION_UDP) + || ((list[x].type == MEMCACHED_CONNECTION_UDP) + && ! (ptr->flags.use_udp)) ) + return MEMCACHED_INVALID_HOST_PROTOCOL; + + WATCHPOINT_ASSERT(list[x].hostname[0] != 0); + memcached_server_create(ptr, &ptr->hosts[ptr->number_of_hosts]); + /* TODO check return type */ + (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], list[x].hostname, + list[x].port, list[x].weight, list[x].type); + ptr->number_of_hosts++; + } + ptr->hosts[0].count= (uint16_t) ptr->number_of_hosts; + + return run_distribution(ptr); +} + +memcached_return_t memcached_server_add_unix_socket(memcached_st *ptr, + const char *filename) +{ + return memcached_server_add_unix_socket_with_weight(ptr, filename, 0); +} + +memcached_return_t memcached_server_add_unix_socket_with_weight(memcached_st *ptr, + const char *filename, + uint32_t weight) +{ + if (!filename) + return MEMCACHED_FAILURE; + + return server_add(ptr, filename, 0, weight, MEMCACHED_CONNECTION_UNIX_SOCKET); +} + +memcached_return_t memcached_server_add_udp(memcached_st *ptr, + const char *hostname, + in_port_t port) +{ + return memcached_server_add_udp_with_weight(ptr, hostname, port, 0); +} + +memcached_return_t memcached_server_add_udp_with_weight(memcached_st *ptr, + const char *hostname, + in_port_t port, + uint32_t weight) +{ + if (!port) + port= MEMCACHED_DEFAULT_PORT; + + if (!hostname) + hostname= "localhost"; + + return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_UDP); +} + +memcached_return_t memcached_server_add(memcached_st *ptr, + const char *hostname, + in_port_t port) +{ + return memcached_server_add_with_weight(ptr, hostname, port, 0); +} + +memcached_return_t memcached_server_add_with_weight(memcached_st *ptr, + const char *hostname, + in_port_t port, + uint32_t weight) +{ + if (!port) + port= MEMCACHED_DEFAULT_PORT; + + if (!hostname) + hostname= "localhost"; + + return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_TCP); +} + +static memcached_return_t server_add(memcached_st *ptr, const char *hostname, + in_port_t port, + uint32_t weight, + memcached_connection_t type) +{ + memcached_server_st *new_host_list; + + if ( (ptr->flags.use_udp && type != MEMCACHED_CONNECTION_UDP) + || ( (type == MEMCACHED_CONNECTION_UDP) && (! ptr->flags.use_udp) ) ) + return MEMCACHED_INVALID_HOST_PROTOCOL; + + new_host_list= ptr->call_realloc(ptr, ptr->hosts, + sizeof(memcached_server_st) * (ptr->number_of_hosts+1)); + + if (new_host_list == NULL) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + ptr->hosts= new_host_list; + + /* TODO: Check return type */ + (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], hostname, port, weight, type); + ptr->number_of_hosts++; + ptr->hosts[0].count= (uint16_t) ptr->number_of_hosts; + + return run_distribution(ptr); +} + +memcached_return_t memcached_server_remove(memcached_server_st *st_ptr) +{ + uint32_t x, host_index; + memcached_st *ptr= st_ptr->root; + memcached_server_st *list= ptr->hosts; + + for (x= 0, host_index= 0; x < ptr->number_of_hosts; x++) + { + if (strncmp(list[x].hostname, st_ptr->hostname, MEMCACHED_MAX_HOST_LENGTH) != 0 || list[x].port != st_ptr->port) + { + if (host_index != x) + memcpy(list+host_index, list+x, sizeof(memcached_server_st)); + host_index++; + } + } + ptr->number_of_hosts= host_index; + + if (st_ptr->address_info) + { + freeaddrinfo(st_ptr->address_info); + st_ptr->address_info= NULL; + } + run_distribution(ptr); + + return MEMCACHED_SUCCESS; +} + +memcached_server_st *memcached_server_list_append(memcached_server_st *ptr, + const char *hostname, in_port_t port, + memcached_return_t *error) +{ + return memcached_server_list_append_with_weight(ptr, hostname, port, 0, error); +} + +memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr, + const char *hostname, in_port_t port, + uint32_t weight, + memcached_return_t *error) +{ + unsigned int count; + memcached_server_st *new_host_list; + + if (hostname == NULL || error == NULL) + return NULL; + + if (!port) + port= MEMCACHED_DEFAULT_PORT; + + /* Increment count for hosts */ + count= 1; + if (ptr != NULL) + { + count+= ptr[0].count; + } + + new_host_list= (memcached_server_st *)realloc(ptr, sizeof(memcached_server_st) * count); + if (!new_host_list) + { + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + + /* TODO: Check return type */ + memcached_server_create_with(NULL, &new_host_list[count-1], hostname, port, weight, MEMCACHED_CONNECTION_TCP); + + /* Backwards compatibility hack */ + new_host_list[0].count= (uint16_t) count; + + *error= MEMCACHED_SUCCESS; + return new_host_list; +} + +unsigned int memcached_server_list_count(memcached_server_st *ptr) +{ + if (ptr == NULL) + return 0; + + return ptr[0].count; +} + +void memcached_server_list_free(memcached_server_st *ptr) +{ + server_list_free(NULL, ptr); +} diff --git a/libmemcached/internal.h b/libmemcached/internal.h new file mode 100644 index 00000000..3e4415dd --- /dev/null +++ b/libmemcached/internal.h @@ -0,0 +1,35 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Internal functions used by the library. Not for public use! + * + */ + +#ifndef LIBMEMCACHED_MEMCACHED_INTERNAL_H +#define LIBMEMCACHED_MEMCACHED_INTERNAL_H + +#if defined(BUILDING_LIBMEMCACHED) + +#ifdef __cplusplus +extern "C" { +#endif + +LIBMEMCACHED_LOCAL +void libmemcached_free(memcached_st *ptr, void *mem); +LIBMEMCACHED_LOCAL +void *libmemcached_malloc(memcached_st *ptr, const size_t size); +LIBMEMCACHED_LOCAL +void *libmemcached_realloc(memcached_st *ptr, void *mem, const size_t size); +LIBMEMCACHED_LOCAL +void *libmemcached_calloc(memcached_st *ptr, size_t nelem, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* BUILDING_LIBMEMCACHED */ +#endif /* LIBMEMCACHED_MEMCACHED_INTERNAL_H */ diff --git a/libmemcached/io.c b/libmemcached/io.c new file mode 100644 index 00000000..8f96bc32 --- /dev/null +++ b/libmemcached/io.c @@ -0,0 +1,643 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Server IO, Not public! + * + */ + + +#include "common.h" +#include +#include + +typedef enum { + MEM_READ, + MEM_WRITE +} memc_read_or_write; + +static ssize_t io_flush(memcached_server_st *ptr, memcached_return_t *error); +static void increment_udp_message_id(memcached_server_st *ptr); + +static memcached_return_t io_wait(memcached_server_st *ptr, + memc_read_or_write read_or_write) +{ + struct pollfd fds= { + .fd= ptr->fd, + .events = POLLIN + }; + int error; + + unlikely (read_or_write == MEM_WRITE) /* write */ + fds.events= POLLOUT; + + /* + ** We are going to block on write, but at least on Solaris we might block + ** on write if we haven't read anything from our input buffer.. + ** Try to purge the input buffer if we don't do any flow control in the + ** application layer (just sending a lot of data etc) + ** The test is moved down in the purge function to avoid duplication of + ** the test. + */ + if (read_or_write == MEM_WRITE) + { + memcached_return_t rc= memcached_purge(ptr); + if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) + return MEMCACHED_FAILURE; + } + + int timeout= ptr->root->poll_timeout; + if (ptr->root->flags.no_block == false) + timeout= -1; + + error= poll(&fds, 1, timeout); + + if (error == 1) + return MEMCACHED_SUCCESS; + else if (error == 0) + return MEMCACHED_TIMEOUT; + + /* Imposssible for anything other then -1 */ + WATCHPOINT_ASSERT(error == -1); + memcached_quit_server(ptr, 1); + + return MEMCACHED_FAILURE; +} + +/** + * Try to fill the input buffer for a server with as much + * data as possible. + * + * @param ptr the server to pack + */ +static bool repack_input_buffer(memcached_server_st *ptr) +{ + if (ptr->read_ptr != ptr->read_buffer) + { + /* Move all of the data to the beginning of the buffer so + ** that we can fit more data into the buffer... + */ + memmove(ptr->read_buffer, ptr->read_ptr, ptr->read_buffer_length); + ptr->read_ptr= ptr->read_buffer; + ptr->read_data_length= ptr->read_buffer_length; + } + + /* There is room in the buffer, try to fill it! */ + if (ptr->read_buffer_length != MEMCACHED_MAX_BUFFER) + { + /* Just try a single read to grab what's available */ + ssize_t nr= read(ptr->fd, + ptr->read_ptr + ptr->read_data_length, + MEMCACHED_MAX_BUFFER - ptr->read_data_length); + + if (nr > 0) + { + ptr->read_data_length+= (size_t)nr; + ptr->read_buffer_length+= (size_t)nr; + return true; + } + } + return false; +} + +/** + * If the we have callbacks connected to this server structure + * we may start process the input queue and fire the callbacks + * for the incomming messages. This function is _only_ called + * when the input buffer is full, so that we _know_ that we have + * at least _one_ message to process. + * + * @param ptr the server to star processing iput messages for + * @return true if we processed anything, false otherwise + */ +static bool process_input_buffer(memcached_server_st *ptr) +{ + /* + ** We might be able to process some of the response messages if we + ** have a callback set up + */ + if (ptr->root->callbacks != NULL && ptr->root->flags.use_udp == false) + { + /* + * We might have responses... try to read them out and fire + * callbacks + */ + memcached_callback_st cb= *ptr->root->callbacks; + + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + memcached_return_t error; + error= memcached_response(ptr, buffer, sizeof(buffer), + &ptr->root->result); + if (error == MEMCACHED_SUCCESS) + { + for (unsigned int x= 0; x < cb.number_of_callback; x++) + { + error= (*cb.callback[x])(ptr->root, &ptr->root->result, cb.context); + if (error != MEMCACHED_SUCCESS) + break; + } + + /* @todo what should I do with the error message??? */ + } + /* @todo what should I do with other error messages?? */ + return true; + } + + return false; +} + +#ifdef UNUSED +void memcached_io_preread(memcached_st *ptr) +{ + unsigned int x; + + return; + + for (x= 0; x < ptr->number_of_hosts; x++) + { + if (memcached_server_response_count(ptr, x) && + ptr->hosts[x].read_data_length < MEMCACHED_MAX_BUFFER ) + { + size_t data_read; + + data_read= read(ptr->hosts[x].fd, + ptr->hosts[x].read_ptr + ptr->hosts[x].read_data_length, + MEMCACHED_MAX_BUFFER - ptr->hosts[x].read_data_length); + if (data_read == -1) + continue; + + ptr->hosts[x].read_buffer_length+= data_read; + ptr->hosts[x].read_data_length+= data_read; + } + } +} +#endif + +memcached_return_t memcached_io_read(memcached_server_st *ptr, + void *buffer, size_t length, ssize_t *nread) +{ + char *buffer_ptr; + + buffer_ptr= buffer; + + while (length) + { + if (!ptr->read_buffer_length) + { + ssize_t data_read; + + while (1) + { + data_read= read(ptr->fd, ptr->read_buffer, MEMCACHED_MAX_BUFFER); + if (data_read > 0) + break; + else if (data_read == -1) + { + ptr->cached_errno= errno; + memcached_return_t rc= MEMCACHED_UNKNOWN_READ_FAILURE; + switch (errno) + { + case EAGAIN: + case EINTR: + if ((rc= io_wait(ptr, MEM_READ)) == MEMCACHED_SUCCESS) + continue; + /* fall through */ + + default: + { + memcached_quit_server(ptr, 1); + *nread= -1; + return rc; + } + } + } + else + { + /* + EOF. Any data received so far is incomplete + so discard it. This always reads by byte in case of TCP + and protocol enforcement happens at memcached_response() + looking for '\n'. We do not care for UDB which requests 8 bytes + at once. Generally, this means that connection went away. Since + for blocking I/O we do not return 0 and for non-blocking case + it will return EGAIN if data is not immediatly available. + */ + memcached_quit_server(ptr, 1); + *nread= -1; + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + + ptr->io_bytes_sent = 0; + ptr->read_data_length= (size_t) data_read; + ptr->read_buffer_length= (size_t) data_read; + ptr->read_ptr= ptr->read_buffer; + } + + if (length > 1) + { + size_t difference; + + difference= (length > ptr->read_buffer_length) ? ptr->read_buffer_length : length; + + memcpy(buffer_ptr, ptr->read_ptr, difference); + length -= difference; + ptr->read_ptr+= difference; + ptr->read_buffer_length-= difference; + buffer_ptr+= difference; + } + else + { + *buffer_ptr= *ptr->read_ptr; + ptr->read_ptr++; + ptr->read_buffer_length--; + buffer_ptr++; + break; + } + } + + ptr->server_failure_counter= 0; + *nread = (ssize_t)(buffer_ptr - (char*)buffer); + return MEMCACHED_SUCCESS; +} + +ssize_t memcached_io_write(memcached_server_st *ptr, + const void *buffer, size_t length, char with_flush) +{ + size_t original_length; + const char* buffer_ptr; + + WATCHPOINT_ASSERT(ptr->fd != -1); + + original_length= length; + buffer_ptr= buffer; + + while (length) + { + char *write_ptr; + size_t should_write; + size_t buffer_end; + + if (ptr->type == MEMCACHED_CONNECTION_UDP) + { + //UDP does not support partial writes + buffer_end= MAX_UDP_DATAGRAM_LENGTH; + should_write= length; + if (ptr->write_buffer_offset + should_write > buffer_end) + return -1; + } + else + { + buffer_end= MEMCACHED_MAX_BUFFER; + should_write= buffer_end - ptr->write_buffer_offset; + should_write= (should_write < length) ? should_write : length; + } + + write_ptr= ptr->write_buffer + ptr->write_buffer_offset; + memcpy(write_ptr, buffer_ptr, should_write); + ptr->write_buffer_offset+= should_write; + buffer_ptr+= should_write; + length-= should_write; + + if (ptr->write_buffer_offset == buffer_end && ptr->type != MEMCACHED_CONNECTION_UDP) + { + memcached_return_t rc; + ssize_t sent_length; + + WATCHPOINT_ASSERT(ptr->fd != -1); + sent_length= io_flush(ptr, &rc); + if (sent_length == -1) + return -1; + + /* If io_flush calls memcached_purge, sent_length may be 0 */ + unlikely (sent_length != 0) + { + WATCHPOINT_ASSERT(sent_length == (ssize_t)buffer_end); + } + } + } + + if (with_flush) + { + memcached_return_t rc; + WATCHPOINT_ASSERT(ptr->fd != -1); + if (io_flush(ptr, &rc) == -1) + return -1; + } + + return (ssize_t) original_length; +} + +memcached_return_t memcached_io_close(memcached_server_st *ptr) +{ + int r; + + if (ptr->fd == -1) + return MEMCACHED_SUCCESS; + + /* in case of death shutdown to avoid blocking at close() */ + if (1) + { + r= shutdown(ptr->fd, SHUT_RDWR); + +#ifdef DEBUG + if (r && errno != ENOTCONN) + { + WATCHPOINT_NUMBER(ptr->fd); + WATCHPOINT_ERRNO(errno); + WATCHPOINT_ASSERT(errno); + } +#endif + } + + r= close(ptr->fd); +#ifdef DEBUG + if (r != 0) + WATCHPOINT_ERRNO(errno); +#endif + + return MEMCACHED_SUCCESS; +} + +memcached_server_st *memcached_io_get_readable_server(memcached_st *memc) +{ +#define MAX_SERVERS_TO_POLL 100 + struct pollfd fds[MAX_SERVERS_TO_POLL]; + unsigned int host_index= 0; + + for (unsigned int x= 0; + x< memc->number_of_hosts && host_index < MAX_SERVERS_TO_POLL; + ++x) + { + if (memc->hosts[x].read_buffer_length > 0) /* I have data in the buffer */ + return &memc->hosts[x]; + + if (memcached_server_response_count(&memc->hosts[x]) > 0) + { + fds[host_index].events = POLLIN; + fds[host_index].revents = 0; + fds[host_index].fd = memc->hosts[x].fd; + ++host_index; + } + } + + if (host_index < 2) + { + /* We have 0 or 1 server with pending events.. */ + for (unsigned int x= 0; x< memc->number_of_hosts; ++x) + if (memcached_server_response_count(&memc->hosts[x]) > 0) + return &memc->hosts[x]; + + return NULL; + } + + int err= poll(fds, host_index, memc->poll_timeout); + switch (err) { + case -1: + memc->cached_errno = errno; + /* FALLTHROUGH */ + case 0: + break; + default: + for (unsigned int x= 0; x < host_index; ++x) + if (fds[x].revents & POLLIN) + for (unsigned int y= 0; y < memc->number_of_hosts; ++y) + if (memc->hosts[y].fd == fds[x].fd) + return &memc->hosts[y]; + } + + return NULL; +} + +static ssize_t io_flush(memcached_server_st *ptr, + memcached_return_t *error) +{ + /* + ** We might want to purge the input buffer if we haven't consumed + ** any output yet... The test for the limits is the purge is inline + ** in the purge function to avoid duplicating the logic.. + */ + { + memcached_return_t rc; + WATCHPOINT_ASSERT(ptr->fd != -1); + rc= memcached_purge(ptr); + + if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) + return -1; + } + ssize_t sent_length; + size_t return_length; + char *local_write_ptr= ptr->write_buffer; + size_t write_length= ptr->write_buffer_offset; + + *error= MEMCACHED_SUCCESS; + + WATCHPOINT_ASSERT(ptr->fd != -1); + + // UDP Sanity check, make sure that we are not sending somthing too big + if (ptr->type == MEMCACHED_CONNECTION_UDP && write_length > MAX_UDP_DATAGRAM_LENGTH) + return -1; + + if (ptr->write_buffer_offset == 0 || (ptr->type == MEMCACHED_CONNECTION_UDP + && ptr->write_buffer_offset == UDP_DATAGRAM_HEADER_LENGTH)) + return 0; + + /* Looking for memory overflows */ +#if defined(DEBUG) + if (write_length == MEMCACHED_MAX_BUFFER) + WATCHPOINT_ASSERT(ptr->write_buffer == local_write_ptr); + WATCHPOINT_ASSERT((ptr->write_buffer + MEMCACHED_MAX_BUFFER) >= (local_write_ptr + write_length)); +#endif + + return_length= 0; + while (write_length) + { + WATCHPOINT_ASSERT(ptr->fd != -1); + WATCHPOINT_ASSERT(write_length > 0); + sent_length= 0; + if (ptr->type == MEMCACHED_CONNECTION_UDP) + increment_udp_message_id(ptr); + sent_length= write(ptr->fd, local_write_ptr, write_length); + + if (sent_length == -1) + { + ptr->cached_errno= errno; + switch (errno) + { + case ENOBUFS: + continue; + case EAGAIN: + { + /* + * We may be blocked on write because the input buffer + * is full. Let's check if we have room in our input + * buffer for more data and retry the write before + * waiting.. + */ + if (repack_input_buffer(ptr) || + process_input_buffer(ptr)) + continue; + + memcached_return_t rc; + rc= io_wait(ptr, MEM_WRITE); + + if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_TIMEOUT) + continue; + + memcached_quit_server(ptr, 1); + return -1; + } + default: + memcached_quit_server(ptr, 1); + *error= MEMCACHED_ERRNO; + return -1; + } + } + + if (ptr->type == MEMCACHED_CONNECTION_UDP && + (size_t)sent_length != write_length) + { + memcached_quit_server(ptr, 1); + return -1; + } + + ptr->io_bytes_sent += (uint32_t) sent_length; + + local_write_ptr+= sent_length; + write_length-= (uint32_t) sent_length; + return_length+= (uint32_t) sent_length; + } + + WATCHPOINT_ASSERT(write_length == 0); + // Need to study this assert() WATCHPOINT_ASSERT(return_length == + // ptr->write_buffer_offset); + + // if we are a udp server, the begining of the buffer is reserverd for + // the upd frame header + if (ptr->type == MEMCACHED_CONNECTION_UDP) + ptr->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; + else + ptr->write_buffer_offset= 0; + + return (ssize_t) return_length; +} + +/* + Eventually we will just kill off the server with the problem. +*/ +void memcached_io_reset(memcached_server_st *ptr) +{ + memcached_quit_server(ptr, 1); +} + +/** + * Read a given number of bytes from the server and place it into a specific + * buffer. Reset the IO channel on this server if an error occurs. + */ +memcached_return_t memcached_safe_read(memcached_server_st *ptr, + void *dta, + size_t size) +{ + size_t offset= 0; + char *data= dta; + + while (offset < size) + { + ssize_t nread; + memcached_return_t rc= memcached_io_read(ptr, data + offset, size - offset, + &nread); + if (rc != MEMCACHED_SUCCESS) + return rc; + + offset+= (size_t) nread; + } + + return MEMCACHED_SUCCESS; +} + +memcached_return_t memcached_io_readline(memcached_server_st *ptr, + char *buffer_ptr, + size_t size) +{ + bool line_complete= false; + size_t total_nr= 0; + + while (!line_complete) + { + if (ptr->read_buffer_length == 0) + { + /* + * We don't have any data in the buffer, so let's fill the read + * buffer. Call the standard read function to avoid duplicating + * the logic. + */ + ssize_t nread; + memcached_return_t rc= memcached_io_read(ptr, buffer_ptr, 1, &nread); + if (rc != MEMCACHED_SUCCESS) + return rc; + + if (*buffer_ptr == '\n') + line_complete= true; + + ++buffer_ptr; + ++total_nr; + } + + /* Now let's look in the buffer and copy as we go! */ + while (ptr->read_buffer_length && total_nr < size && !line_complete) + { + *buffer_ptr = *ptr->read_ptr; + if (*buffer_ptr == '\n') + line_complete = true; + --ptr->read_buffer_length; + ++ptr->read_ptr; + ++total_nr; + ++buffer_ptr; + } + + if (total_nr == size) + return MEMCACHED_PROTOCOL_ERROR; + } + + return MEMCACHED_SUCCESS; +} + +/* + * The udp request id consists of two seperate sections + * 1) The thread id + * 2) The message number + * The thread id should only be set when the memcached_st struct is created + * and should not be changed. + * + * The message num is incremented for each new message we send, this function + * extracts the message number from message_id, increments it and then + * writes the new value back into the header + */ +static void increment_udp_message_id(memcached_server_st *ptr) +{ + struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; + uint16_t cur_req= get_udp_datagram_request_id(header); + int msg_num= get_msg_num_from_request_id(cur_req); + int thread_id= get_thread_id_from_request_id(cur_req); + + if (((++msg_num) & UDP_REQUEST_ID_THREAD_MASK) != 0) + msg_num= 0; + + header->request_id= htons((uint16_t) (thread_id | msg_num)); +} + +memcached_return_t memcached_io_init_udp_header(memcached_server_st *ptr, uint16_t thread_id) +{ + if (thread_id > UDP_REQUEST_ID_MAX_THREAD_ID) + return MEMCACHED_FAILURE; + + struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; + header->request_id= htons((uint16_t) (generate_udp_request_thread_id(thread_id))); + header->num_datagrams= htons(1); + header->sequence_number= htons(0); + + return MEMCACHED_SUCCESS; +} diff --git a/libmemcached/io.h b/libmemcached/io.h new file mode 100644 index 00000000..03d63b55 --- /dev/null +++ b/libmemcached/io.h @@ -0,0 +1,71 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Server IO, Not public! + * + */ + +#ifndef LIBMEMCACHED_MEMCACHED_IO_H +#define LIBMEMCACHED_MEMCACHED_IO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(BUILDING_LIBMEMCACHED) + +#include "libmemcached/memcached.h" + +#define MAX_UDP_DATAGRAM_LENGTH 1400 +#define UDP_DATAGRAM_HEADER_LENGTH 8 +#define UDP_REQUEST_ID_MSG_SIG_DIGITS 10 +#define UDP_REQUEST_ID_THREAD_MASK 0xFFFF << UDP_REQUEST_ID_MSG_SIG_DIGITS +#define get_udp_datagram_request_id(A) ntohs((A)->request_id) +#define get_udp_datagram_seq_num(A) ntohs((A)->sequence_number) +#define get_udp_datagram_num_datagrams(A) ntohs((A)->num_datagrams) +#define get_msg_num_from_request_id(A) ( (A) & (~(UDP_REQUEST_ID_THREAD_MASK)) ) +#define get_thread_id_from_request_id(A) ( (A) & (UDP_REQUEST_ID_THREAD_MASK) ) >> UDP_REQUEST_ID_MSG_SIG_DIGITS +#define generate_udp_request_thread_id(A) (A) << UDP_REQUEST_ID_MSG_SIG_DIGITS +#define UDP_REQUEST_ID_MAX_THREAD_ID get_thread_id_from_request_id(0xFFFF) + +struct udp_datagram_header_st { + uint16_t request_id; + uint16_t sequence_number; + uint16_t num_datagrams; + uint16_t reserved; +}; + +ssize_t memcached_io_write(memcached_server_st *ptr, + const void *buffer, size_t length, char with_flush); +void memcached_io_reset(memcached_server_st *ptr); +memcached_return_t memcached_io_read(memcached_server_st *ptr, + void *buffer, size_t length, ssize_t *nread); +/* Read a line (terminated by '\n') into the buffer */ +memcached_return_t memcached_io_readline(memcached_server_st *ptr, + char *buffer_ptr, + size_t size); +memcached_return_t memcached_io_close(memcached_server_st *ptr); +/* Read n bytes of data from the server and store them in dta */ +memcached_return_t memcached_safe_read(memcached_server_st *ptr, + void *dta, + size_t size); +/* Read a single response from the server */ +memcached_return_t memcached_read_one_response(memcached_server_st *ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result); +memcached_return_t memcached_io_init_udp_header(memcached_server_st *ptr, + uint16_t thread_id); + +memcached_server_st *memcached_io_get_readable_server(memcached_st *memc); + +#endif /* BUILDING_LIBMEMCACHED */ + +#ifdef __cplusplus +} +#endif + +#endif /* LIBMEMCACHED_MEMCACHED_IO_H */ diff --git a/libmemcached/key.c b/libmemcached/key.c new file mode 100644 index 00000000..3b16b273 --- /dev/null +++ b/libmemcached/key.c @@ -0,0 +1,27 @@ +#include "common.h" + +memcached_return_t memcached_key_test(const char * const *keys, + const size_t *key_length, + size_t number_of_keys) +{ + uint32_t x; + memcached_return_t rc; + + for (x= 0; x < number_of_keys; x++) + { + size_t y; + + rc= memcached_validate_key_length(*(key_length + x), false); + if (rc != MEMCACHED_SUCCESS) + return rc; + + for (y= 0; y < *(key_length + x); y++) + { + if ((isgraph(keys[x][y])) == 0) + return MEMCACHED_BAD_KEY_PROVIDED; + } + } + + return MEMCACHED_SUCCESS; +} + diff --git a/libmemcached/memcached.h b/libmemcached/memcached.h index 39158498..efe16e0c 100644 --- a/libmemcached/memcached.h +++ b/libmemcached/memcached.h @@ -25,15 +25,16 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +// Everything above this line must be in the order specified. +#include +#include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/libmemcached/memcached_allocators.c b/libmemcached/memcached_allocators.c deleted file mode 100644 index 767371cb..00000000 --- a/libmemcached/memcached_allocators.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "common.h" - -void libmemcached_free(memcached_st *ptr, void *mem) -{ - (void) ptr; - free(mem); -} - -void *libmemcached_malloc(memcached_st *ptr, size_t size) -{ - (void) ptr; - return malloc(size); -} - -void *libmemcached_realloc(memcached_st *ptr, void *mem, size_t size) -{ - (void) ptr; - return realloc(mem, size); -} - -void *libmemcached_calloc(memcached_st *ptr, size_t nelem, size_t size) -{ - if (ptr->call_malloc != libmemcached_malloc) - { - void *ret = libmemcached_malloc(ptr, nelem * size); - if (ret != NULL) - memset(ret, 0, nelem * size); - - return ret; - } - - return calloc(nelem, size); -} - -memcached_return_t memcached_set_memory_allocators(memcached_st *ptr, - memcached_malloc_fn mem_malloc, - memcached_free_fn mem_free, - memcached_realloc_fn mem_realloc, - memcached_calloc_fn mem_calloc) -{ - /* All should be set, or none should be set */ - if (mem_malloc == NULL && mem_free == NULL && mem_realloc == NULL && mem_calloc == NULL) - { - ptr->call_malloc= libmemcached_malloc; - ptr->call_free= libmemcached_free; - ptr->call_realloc= libmemcached_realloc; - ptr->call_calloc= libmemcached_calloc; - } - else if (mem_malloc == NULL || mem_free == NULL || mem_realloc == NULL || mem_calloc == NULL) - return MEMCACHED_FAILURE; - else - { - ptr->call_malloc= mem_malloc; - ptr->call_free= mem_free; - ptr->call_realloc= mem_realloc; - ptr->call_calloc= mem_calloc; - } - - return MEMCACHED_SUCCESS; -} - -void memcached_get_memory_allocators(memcached_st *ptr, - memcached_malloc_fn *mem_malloc, - memcached_free_fn *mem_free, - memcached_realloc_fn *mem_realloc, - memcached_calloc_fn *mem_calloc) -{ - *mem_malloc= ptr->call_malloc; - *mem_free= ptr->call_free; - *mem_realloc= ptr->call_realloc; - *mem_calloc= ptr->call_calloc; -} diff --git a/libmemcached/memcached_analyze.c b/libmemcached/memcached_analyze.c deleted file mode 100644 index 5c1c7e47..00000000 --- a/libmemcached/memcached_analyze.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "common.h" - -static void calc_largest_consumption(memcached_analysis_st *result, - const uint32_t server_num, - const uint64_t nbytes) -{ - if (result->most_used_bytes < nbytes) - { - result->most_used_bytes= nbytes; - result->most_consumed_server= server_num; - } -} - -static void calc_oldest_node(memcached_analysis_st *result, - const uint32_t server_num, - const uint32_t uptime) -{ - if (result->longest_uptime < uptime) - { - result->longest_uptime= uptime; - result->oldest_server= server_num; - } -} - -static void calc_least_free_node(memcached_analysis_st *result, - const uint32_t server_num, - const uint64_t max_allowed_bytes, - const uint64_t used_bytes) -{ - uint64_t remaining_bytes= max_allowed_bytes - used_bytes; - - if (result->least_remaining_bytes == 0 || - remaining_bytes < result->least_remaining_bytes) - { - result->least_remaining_bytes= remaining_bytes; - result->least_free_server= server_num; - } -} - -static void calc_average_item_size(memcached_analysis_st *result, - const uint64_t total_items, - const uint64_t total_bytes) -{ - if (total_items > 0 && total_bytes > 0) - result->average_item_size= (uint32_t) (total_bytes / total_items); -} - -static void calc_hit_ratio(memcached_analysis_st *result, - const uint64_t total_get_hits, - const uint64_t total_get_cmds) -{ - if (total_get_hits == 0 || total_get_cmds == 0) - { - result->pool_hit_ratio= 0; - return; - } - - double temp= (double) (total_get_hits/total_get_cmds); - result->pool_hit_ratio= temp * 100; -} - -memcached_analysis_st *memcached_analyze(memcached_st *memc, - memcached_stat_st *memc_stat, - memcached_return_t *error) -{ - uint64_t total_items= 0, total_bytes= 0; - uint64_t total_get_cmds= 0, total_get_hits= 0; - uint32_t server_count, x; - memcached_analysis_st *result; - - *error= MEMCACHED_SUCCESS; - server_count= memcached_server_count(memc); - result= (memcached_analysis_st*)calloc(memc->number_of_hosts, - sizeof(memcached_analysis_st)); - - if (!result) - { - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return NULL; - } - - for (x= 0; x < server_count; x++) - { - calc_largest_consumption(result, x, memc_stat[x].bytes); - calc_oldest_node(result, x, memc_stat[x].uptime); - calc_least_free_node(result, x, - memc_stat[x].limit_maxbytes, - memc_stat[x].bytes); - - total_get_hits+= memc_stat[x].get_hits; - total_get_cmds+= memc_stat[x].cmd_get; - total_items+= memc_stat[x].curr_items; - total_bytes+= memc_stat[x].bytes; - } - - calc_average_item_size(result, total_items, total_bytes); - calc_hit_ratio(result, total_get_hits, total_get_cmds); - - return result; -} diff --git a/libmemcached/memcached_auto.c b/libmemcached/memcached_auto.c deleted file mode 100644 index b7466404..00000000 --- a/libmemcached/memcached_auto.c +++ /dev/null @@ -1,275 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Methods for adding or decrementing values from an object in memcached - * - */ - -#include "common.h" - -static memcached_return_t memcached_auto(memcached_st *ptr, - const char *verb, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - uint64_t offset, - uint64_t *value) -{ - size_t send_length; - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - unsigned int server_key; - bool no_reply= ptr->flags.no_reply; - - unlikely (ptr->hosts == NULL || ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - - server_key= memcached_generate_hash(ptr, master_key, master_key_length); - - send_length= (size_t)snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%s %s%.*s %" PRIu64 "%s\r\n", verb, - ptr->prefix_key, - (int)key_length, key, - offset, no_reply ? " noreply" : ""); - unlikely (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) - return MEMCACHED_WRITE_FAILURE; - - rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, 1); - if (no_reply || rc != MEMCACHED_SUCCESS) - return rc; - - rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - /* - So why recheck responce? Because the protocol is brain dead :) - The number returned might end up equaling one of the string - values. Less chance of a mistake with strncmp() so we will - use it. We still called memcached_response() though since it - worked its magic for non-blocking IO. - */ - if (!strncmp(buffer, "ERROR\r\n", 7)) - { - *value= 0; - rc= MEMCACHED_PROTOCOL_ERROR; - } - else if (!strncmp(buffer, "NOT_FOUND\r\n", 11)) - { - *value= 0; - rc= MEMCACHED_NOTFOUND; - } - else - { - *value= strtoull(buffer, (char **)NULL, 10); - rc= MEMCACHED_SUCCESS; - } - - return rc; -} - -static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - uint64_t offset, uint64_t initial, - uint32_t expiration, - uint64_t *value) -{ - unsigned int server_key; - bool no_reply= ptr->flags.no_reply; - - unlikely (ptr->hosts == NULL || ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - server_key= memcached_generate_hash(ptr, master_key, master_key_length); - - if (no_reply) - { - if(cmd == PROTOCOL_BINARY_CMD_DECREMENT) - cmd= PROTOCOL_BINARY_CMD_DECREMENTQ; - if(cmd == PROTOCOL_BINARY_CMD_INCREMENT) - cmd= PROTOCOL_BINARY_CMD_INCREMENTQ; - } - protocol_binary_request_incr request= {.bytes= {0}}; - - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= cmd; - request.message.header.request.keylen= htons((uint16_t) key_length); - request.message.header.request.extlen= 20; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl((uint32_t) (key_length + request.message.header.request.extlen)); - request.message.body.delta= htonll(offset); - request.message.body.initial= htonll(initial); - request.message.body.expiration= htonl((uint32_t) expiration); - - if ((memcached_do(&ptr->hosts[server_key], request.bytes, - sizeof(request.bytes), 0)!=MEMCACHED_SUCCESS) || - (memcached_io_write(&ptr->hosts[server_key], key, key_length, 1) == -1)) - { - memcached_io_reset(&ptr->hosts[server_key]); - return MEMCACHED_WRITE_FAILURE; - } - - if (no_reply) - return MEMCACHED_SUCCESS; - return memcached_response(&ptr->hosts[server_key], (char*)value, sizeof(*value), NULL); -} - -memcached_return_t memcached_increment(memcached_st *ptr, - const char *key, size_t key_length, - uint32_t offset, - uint64_t *value) -{ - return memcached_increment_by_key(ptr, key, key_length, key, key_length, offset, value); -} - -memcached_return_t memcached_decrement(memcached_st *ptr, - const char *key, size_t key_length, - uint32_t offset, - uint64_t *value) -{ - return memcached_decrement_by_key(ptr, key, key_length, key, key_length, offset, value); -} - -memcached_return_t memcached_increment_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - uint64_t offset, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - LIBMEMCACHED_MEMCACHED_INCREMENT_START(); - if (ptr->flags.binary_protocol) - { - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, - master_key, master_key_length, key, key_length, - (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, - value); - } - else - { - rc= memcached_auto(ptr, "incr", master_key, master_key_length, key, key_length, offset, value); - } - - LIBMEMCACHED_MEMCACHED_INCREMENT_END(); - - return rc; -} - -memcached_return_t memcached_decrement_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - uint64_t offset, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - LIBMEMCACHED_MEMCACHED_DECREMENT_START(); - if (ptr->flags.binary_protocol) - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, - master_key, master_key_length, key, key_length, - (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, - value); - else - rc= memcached_auto(ptr, "decr", master_key, master_key_length, key, key_length, offset, value); - - LIBMEMCACHED_MEMCACHED_DECREMENT_END(); - - return rc; -} - -memcached_return_t memcached_increment_with_initial(memcached_st *ptr, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - return memcached_increment_with_initial_by_key(ptr, key, key_length, - key, key_length, - offset, initial, expiration, value); -} - -memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); - if (ptr->flags.binary_protocol) - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT, - master_key, master_key_length, key, key_length, - offset, initial, (uint32_t)expiration, - value); - else - rc= MEMCACHED_PROTOCOL_ERROR; - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); - - return rc; -} - -memcached_return_t memcached_decrement_with_initial(memcached_st *ptr, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - return memcached_decrement_with_initial_by_key(ptr, key, key_length, - key, key_length, - offset, initial, expiration, value); -} - -memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char *key, - size_t key_length, - uint64_t offset, - uint64_t initial, - time_t expiration, - uint64_t *value) -{ - memcached_return_t rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START(); - if (ptr->flags.binary_protocol) - { - rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT, - master_key, master_key_length, key, key_length, - offset, initial, (uint32_t)expiration, - value); - } - else - { - rc= MEMCACHED_PROTOCOL_ERROR; - } - - LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END(); - - return rc; -} - diff --git a/libmemcached/memcached_behavior.c b/libmemcached/memcached_behavior.c deleted file mode 100644 index 37c9f529..00000000 --- a/libmemcached/memcached_behavior.c +++ /dev/null @@ -1,314 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Change the behavior of the memcached connection. - * - */ - -#include "common.h" -#include -#include -#include -#include - -/* - This function is used to modify the behavior of running client. - - We quit all connections so we can reset the sockets. -*/ - -memcached_return_t memcached_behavior_set(memcached_st *ptr, - memcached_behavior_t flag, - uint64_t data) -{ - switch (flag) - { - case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: - ptr->number_of_replicas= (uint32_t)data; - break; - case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: - ptr->io_msg_watermark= (uint32_t) data; - break; - case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: - ptr->io_bytes_watermark= (uint32_t)data; - break; - case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: - ptr->io_key_prefetch = (uint32_t)data; - break; - case MEMCACHED_BEHAVIOR_SND_TIMEOUT: - ptr->snd_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: - ptr->rcv_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: - ptr->server_failure_limit= (uint32_t)data; - break; - case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: - if (data) - ptr->flags.verify_key= false; - - ptr->flags.binary_protocol= data ? true : false; - break; - case MEMCACHED_BEHAVIOR_SUPPORT_CAS: - ptr->flags.support_cas= data ? true: false; - break; - case MEMCACHED_BEHAVIOR_NO_BLOCK: - ptr->flags.no_block= data ? true: false; - memcached_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: - ptr->flags.buffer_requests= data ? true : false; - memcached_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_USE_UDP: - if (ptr->number_of_hosts) - return MEMCACHED_FAILURE; - ptr->flags.use_udp= data ? true : false; - - if (data) - ptr->flags.no_reply= data ? true : false; - break; - - case MEMCACHED_BEHAVIOR_TCP_NODELAY: - ptr->flags.tcp_nodelay= data ? true : false; - memcached_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_DISTRIBUTION: - { - ptr->distribution= (memcached_server_distribution_t)(data); - if (ptr->distribution == MEMCACHED_DISTRIBUTION_RANDOM) - { - srandom((uint32_t) time(NULL)); - } - run_distribution(ptr); - break; - } - case MEMCACHED_BEHAVIOR_KETAMA: - { - if (data) - { - ptr->hash= MEMCACHED_HASH_MD5; - ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA; - } - else - { - ptr->hash= 0; - ptr->distribution= 0; - } - run_distribution(ptr); - break; - } - case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: - { - ptr->hash= MEMCACHED_HASH_MD5; - ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA; - ptr->flags.ketama_weighted= data ? true : false; - run_distribution(ptr); - break; - } - case MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE: - switch (data) - { - case MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED: - ptr->hash= MEMCACHED_HASH_MD5; - ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA; - break; - case MEMCACHED_KETAMA_COMPAT_SPY: - ptr->hash= MEMCACHED_HASH_MD5; - ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY; - break; - default: - return MEMCACHED_FAILURE; - } - run_distribution(ptr); - break; - case MEMCACHED_BEHAVIOR_HASH: -#ifndef HAVE_HSIEH_HASH - if ((memcached_hash_t)(data) == MEMCACHED_HASH_HSIEH) - return MEMCACHED_FAILURE; -#endif - ptr->hash= (memcached_hash_t)(data); - break; - case MEMCACHED_BEHAVIOR_KETAMA_HASH: - ptr->hash_continuum= (memcached_hash_t)(data); - run_distribution(ptr); - break; - case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: - ptr->flags.use_cache_lookups= data ? true : false; - memcached_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_VERIFY_KEY: - if (ptr->flags.binary_protocol) - break; - ptr->flags.verify_key= data ? true : false; - break; - case MEMCACHED_BEHAVIOR_SORT_HOSTS: - { - ptr->flags.use_sort_hosts= data ? true : false; - run_distribution(ptr); - - break; - } - case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: - ptr->poll_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: - ptr->connect_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: - ptr->retry_timeout= (int32_t)data; - break; - case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: - ptr->send_size= (int32_t)data; - memcached_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: - ptr->recv_size= (int32_t)data; - memcached_quit(ptr); - break; - case MEMCACHED_BEHAVIOR_USER_DATA: - return MEMCACHED_FAILURE; - case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: - ptr->flags.hash_with_prefix_key= data ? true : false; - break; - case MEMCACHED_BEHAVIOR_NOREPLY: - ptr->flags.no_reply= data ? true : false; - break; - case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: - ptr->flags.auto_eject_hosts= data ? true : false; - break; - case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: - srandom((uint32_t) time(NULL)); - ptr->flags.randomize_replica_read= data ? true : false; - break; - default: - /* Shouldn't get here */ - WATCHPOINT_ASSERT(flag); - break; - } - - return MEMCACHED_SUCCESS; -} - -uint64_t memcached_behavior_get(memcached_st *ptr, - memcached_behavior_t flag) -{ - switch (flag) - { - case MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS: - return ptr->number_of_replicas; - case MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK: - return ptr->io_msg_watermark; - case MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK: - return ptr->io_bytes_watermark; - case MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH: - return ptr->io_key_prefetch; - case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL: - return ptr->flags.binary_protocol; - case MEMCACHED_BEHAVIOR_SUPPORT_CAS: - return ptr->flags.support_cas; - case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: - return ptr->flags.use_cache_lookups; - case MEMCACHED_BEHAVIOR_NO_BLOCK: - return ptr->flags.no_block; - case MEMCACHED_BEHAVIOR_BUFFER_REQUESTS: - return ptr->flags.buffer_requests; - case MEMCACHED_BEHAVIOR_USE_UDP: - return ptr->flags.use_udp; - case MEMCACHED_BEHAVIOR_TCP_NODELAY: - return ptr->flags.tcp_nodelay; - case MEMCACHED_BEHAVIOR_VERIFY_KEY: - return ptr->flags.verify_key; - case MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED: - return ptr->flags.ketama_weighted; - case MEMCACHED_BEHAVIOR_DISTRIBUTION: - return ptr->distribution; - case MEMCACHED_BEHAVIOR_KETAMA: - return (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA) ? (uint64_t) 1 : 0; - case MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE: - switch (ptr->distribution) - { - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: - return MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED; - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: - return MEMCACHED_KETAMA_COMPAT_SPY; - case MEMCACHED_DISTRIBUTION_MODULA: - case MEMCACHED_DISTRIBUTION_CONSISTENT: - case MEMCACHED_DISTRIBUTION_RANDOM: - default: - return (uint64_t)-1; - } - /* NOTREACHED */ - case MEMCACHED_BEHAVIOR_HASH: - return ptr->hash; - case MEMCACHED_BEHAVIOR_KETAMA_HASH: - return ptr->hash_continuum; - case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT: - return ptr->server_failure_limit; - case MEMCACHED_BEHAVIOR_SORT_HOSTS: - return ptr->flags.use_sort_hosts; - case MEMCACHED_BEHAVIOR_POLL_TIMEOUT: - return (uint64_t)ptr->poll_timeout; - case MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT: - return (uint64_t)ptr->connect_timeout; - case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT: - return (uint64_t)ptr->retry_timeout; - case MEMCACHED_BEHAVIOR_SND_TIMEOUT: - return (uint64_t)ptr->snd_timeout; - case MEMCACHED_BEHAVIOR_RCV_TIMEOUT: - return (uint64_t)ptr->rcv_timeout; - case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE: - { - int sock_size; - socklen_t sock_length= sizeof(int); - - /* REFACTOR */ - /* We just try the first host, and if it is down we return zero */ - if ((memcached_connect(&ptr->hosts[0])) != MEMCACHED_SUCCESS) - return 0; - - if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET, - SO_SNDBUF, &sock_size, &sock_length)) - return 0; /* Zero means error */ - - return (uint64_t) sock_size; - } - case MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE: - { - int sock_size; - socklen_t sock_length= sizeof(int); - - /* REFACTOR */ - /* We just try the first host, and if it is down we return zero */ - if ((memcached_connect(&ptr->hosts[0])) != MEMCACHED_SUCCESS) - return 0; - - if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET, - SO_RCVBUF, &sock_size, &sock_length)) - return 0; /* Zero means error */ - - return (uint64_t) sock_size; - } - case MEMCACHED_BEHAVIOR_USER_DATA: - return MEMCACHED_FAILURE; - case MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY: - return ptr->flags.hash_with_prefix_key; - case MEMCACHED_BEHAVIOR_NOREPLY: - return ptr->flags.no_reply; - case MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS: - return ptr->flags.auto_eject_hosts; - case MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ: - return ptr->flags.randomize_replica_read; - default: - WATCHPOINT_ASSERT(flag); - break; - } - - WATCHPOINT_ASSERT(0); /* Programming mistake if it gets this far */ - return 0; -} diff --git a/libmemcached/memcached_behavior.h b/libmemcached/memcached_behavior.h deleted file mode 100644 index 14cbc3aa..00000000 --- a/libmemcached/memcached_behavior.h +++ /dev/null @@ -1,30 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Change the behavior of the memcached connection. - * - */ - -#ifndef __MEMCACHED_BEHAVIOR_H__ -#define __MEMCACHED_BEHAVIOR_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -LIBMEMCACHED_API -memcached_return_t memcached_behavior_set(memcached_st *ptr, memcached_behavior_t flag, uint64_t data); - -LIBMEMCACHED_API -uint64_t memcached_behavior_get(memcached_st *ptr, memcached_behavior_t flag); - - -#ifdef __cplusplus -} -#endif - -#endif /* __MEMCACHED_BEHAVIOR_H__ */ diff --git a/libmemcached/memcached_callback.c b/libmemcached/memcached_callback.c deleted file mode 100644 index b0ddfc2b..00000000 --- a/libmemcached/memcached_callback.c +++ /dev/null @@ -1,186 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Change any of the possible callbacks. - * - */ - -#include "common.h" -#include -#include -#include - -/* - These functions provide data and function callback support -*/ - -memcached_return_t memcached_callback_set(memcached_st *ptr, - memcached_callback_t flag, - void *data) -{ - switch (flag) - { - case MEMCACHED_CALLBACK_PREFIX_KEY: - { - char *key= (char *)data; - - if (key) - { - size_t key_length= strlen(key); - - if (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED) - { - return MEMCACHED_BAD_KEY_PROVIDED; - } - - if ((key_length > MEMCACHED_PREFIX_KEY_MAX_SIZE -1) - || (strcpy(ptr->prefix_key, key) == NULL)) - { - ptr->prefix_key_length= 0; - return MEMCACHED_BAD_KEY_PROVIDED; - } - else - { - ptr->prefix_key_length= key_length; - } - } - else - { - memset(ptr->prefix_key, 0, MEMCACHED_PREFIX_KEY_MAX_SIZE); - ptr->prefix_key_length= 0; - } - - break; - } - case MEMCACHED_CALLBACK_USER_DATA: - { - ptr->user_data= data; - break; - } - case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: - { - memcached_cleanup_fn func= *(memcached_cleanup_fn *)&data; - ptr->on_cleanup= func; - break; - } - case MEMCACHED_CALLBACK_CLONE_FUNCTION: - { - memcached_clone_fn func= *(memcached_clone_fn *)&data; - ptr->on_clone= func; - break; - } -#ifdef MEMCACHED_ENABLE_DEPRECATED - case MEMCACHED_CALLBACK_MALLOC_FUNCTION: - { - memcached_malloc_function func= *(memcached_malloc_fn *)&data; - ptr->call_malloc= func; - break; - } - case MEMCACHED_CALLBACK_REALLOC_FUNCTION: - { - memcached_realloc_function func= *(memcached_realloc_fn *)&data; - ptr->call_realloc= func; - break; - } - case MEMCACHED_CALLBACK_FREE_FUNCTION: - { - memcached_free_function func= *(memcached_free_fn *)&data; - ptr->call_free= func; - break; - } -#endif - case MEMCACHED_CALLBACK_GET_FAILURE: - { - memcached_trigger_key_fn func= *(memcached_trigger_key_fn *)&data; - ptr->get_key_failure= func; - break; - } - case MEMCACHED_CALLBACK_DELETE_TRIGGER: - { - memcached_trigger_delete_key_fn func= *(memcached_trigger_delete_key_fn *)&data; - ptr->delete_trigger= func; - break; - } - default: - return MEMCACHED_FAILURE; - } - - return MEMCACHED_SUCCESS; -} - -void *memcached_callback_get(memcached_st *ptr, - memcached_callback_t flag, - memcached_return_t *error) -{ - memcached_return_t local_error; - - if (!error) - error = &local_error; - - switch (flag) - { - case MEMCACHED_CALLBACK_PREFIX_KEY: - { - if (ptr->prefix_key[0] == 0) - { - *error= MEMCACHED_FAILURE; - return NULL; - } - else - { - *error= MEMCACHED_SUCCESS; - return (void *)ptr->prefix_key; - } - } - case MEMCACHED_CALLBACK_USER_DATA: - { - *error= ptr->user_data ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return (void *)ptr->user_data; - } - case MEMCACHED_CALLBACK_CLEANUP_FUNCTION: - { - *error= ptr->on_cleanup ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->on_cleanup; - } - case MEMCACHED_CALLBACK_CLONE_FUNCTION: - { - *error= ptr->on_clone ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->on_clone; - } -#ifdef MEMCACHED_ENABLE_DEPRECATED - case MEMCACHED_CALLBACK_MALLOC_FUNCTION: - { - *error= ptr->call_malloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->call_malloc; - } - case MEMCACHED_CALLBACK_REALLOC_FUNCTION: - { - *error= ptr->call_realloc ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->call_realloc; - } - case MEMCACHED_CALLBACK_FREE_FUNCTION: - { - *error= ptr->call_free ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->call_free; - } -#endif - case MEMCACHED_CALLBACK_GET_FAILURE: - { - *error= ptr->get_key_failure ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->get_key_failure; - } - case MEMCACHED_CALLBACK_DELETE_TRIGGER: - { - *error= ptr->delete_trigger ? MEMCACHED_SUCCESS : MEMCACHED_FAILURE; - return *(void **)&ptr->delete_trigger; - } - default: - WATCHPOINT_ASSERT(0); - *error= MEMCACHED_FAILURE; - return NULL; - } -} diff --git a/libmemcached/memcached_callback.h b/libmemcached/memcached_callback.h deleted file mode 100644 index 0719a66f..00000000 --- a/libmemcached/memcached_callback.h +++ /dev/null @@ -1,32 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Change any of the possible callbacks. - * - */ - -#ifndef __MEMCACHED_CALLBACK_H__ -#define __MEMCACHED_CALLBACK_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -LIBMEMCACHED_API -memcached_return_t memcached_callback_set(memcached_st *ptr, - memcached_callback_t flag, - void *data); -LIBMEMCACHED_API -void *memcached_callback_get(memcached_st *ptr, - memcached_callback_t flag, - memcached_return_t *error); - -#ifdef __cplusplus -} -#endif - -#endif /* __MEMCACHED_CALLBACK_H__ */ diff --git a/libmemcached/memcached_connect.c b/libmemcached/memcached_connect.c deleted file mode 100644 index 24328f4c..00000000 --- a/libmemcached/memcached_connect.c +++ /dev/null @@ -1,355 +0,0 @@ -#include "common.h" -#include -#include -#include - -static memcached_return_t set_hostinfo(memcached_server_st *server) -{ - struct addrinfo *ai; - struct addrinfo hints; - int e; - char str_port[NI_MAXSERV]; - - sprintf(str_port, "%u", server->port); - - memset(&hints, 0, sizeof(hints)); - - // hints.ai_family= AF_INET; - if (server->type == MEMCACHED_CONNECTION_UDP) - { - hints.ai_protocol= IPPROTO_UDP; - hints.ai_socktype= SOCK_DGRAM; - } - else - { - hints.ai_socktype= SOCK_STREAM; - hints.ai_protocol= IPPROTO_TCP; - } - - e= getaddrinfo(server->hostname, str_port, &hints, &ai); - if (e != 0) - { - WATCHPOINT_STRING(server->hostname); - WATCHPOINT_STRING(gai_strerror(e)); - return MEMCACHED_HOST_LOOKUP_FAILURE; - } - - if (server->address_info) - { - freeaddrinfo(server->address_info); - server->address_info= NULL; - } - server->address_info= ai; - - return MEMCACHED_SUCCESS; -} - -static memcached_return_t set_socket_options(memcached_server_st *ptr) -{ - WATCHPOINT_ASSERT(ptr->fd != -1); - - if (ptr->type == MEMCACHED_CONNECTION_UDP) - return MEMCACHED_SUCCESS; - -#ifdef HAVE_SNDTIMEO - if (ptr->root->snd_timeout) - { - int error; - struct timeval waittime; - - waittime.tv_sec= 0; - waittime.tv_usec= ptr->root->snd_timeout; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, - &waittime, (socklen_t)sizeof(struct timeval)); - WATCHPOINT_ASSERT(error == 0); - } -#endif - -#ifdef HAVE_RCVTIMEO - if (ptr->root->rcv_timeout) - { - int error; - struct timeval waittime; - - waittime.tv_sec= 0; - waittime.tv_usec= ptr->root->rcv_timeout; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, - &waittime, (socklen_t)sizeof(struct timeval)); - WATCHPOINT_ASSERT(error == 0); - } -#endif - - if (ptr->root->flags.no_block) - { - int error; - struct linger linger; - - linger.l_onoff= 1; - linger.l_linger= 0; /* By default on close() just drop the socket */ - error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, - &linger, (socklen_t)sizeof(struct linger)); - WATCHPOINT_ASSERT(error == 0); - } - - if (ptr->root->flags.tcp_nodelay) - { - int flag= 1; - int error; - - error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, - &flag, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - } - - if (ptr->root->send_size) - { - int error; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, - &ptr->root->send_size, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - } - - if (ptr->root->recv_size) - { - int error; - - error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF, - &ptr->root->recv_size, (socklen_t)sizeof(int)); - WATCHPOINT_ASSERT(error == 0); - } - - /* libmemcached will always use nonblocking IO to avoid write deadlocks */ - int flags; - - do - flags= fcntl(ptr->fd, F_GETFL, 0); - while (flags == -1 && (errno == EINTR || errno == EAGAIN)); - - unlikely (flags == -1) - return MEMCACHED_CONNECTION_FAILURE; - else if ((flags & O_NONBLOCK) == 0) - { - int rval; - - do - rval= fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK); - while (rval == -1 && (errno == EINTR || errno == EAGAIN)); - - unlikely (rval == -1) - return MEMCACHED_CONNECTION_FAILURE; - } - - return MEMCACHED_SUCCESS; -} - -static memcached_return_t unix_socket_connect(memcached_server_st *ptr) -{ - struct sockaddr_un servAddr; - socklen_t addrlen; - - if (ptr->fd == -1) - { - if ((ptr->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) - { - ptr->cached_errno= errno; - return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; - } - - memset(&servAddr, 0, sizeof (struct sockaddr_un)); - servAddr.sun_family= AF_UNIX; - strcpy(servAddr.sun_path, ptr->hostname); /* Copy filename */ - - addrlen= (socklen_t) (strlen(servAddr.sun_path) + sizeof(servAddr.sun_family)); - -test_connect: - if (connect(ptr->fd, - (struct sockaddr *)&servAddr, - sizeof(servAddr)) < 0) - { - switch (errno) - { - case EINPROGRESS: - case EALREADY: - case EINTR: - goto test_connect; - case EISCONN: /* We were spinning waiting on connect */ - break; - default: - WATCHPOINT_ERRNO(errno); - ptr->cached_errno= errno; - return MEMCACHED_ERRNO; - } - } - } - - WATCHPOINT_ASSERT(ptr->fd != -1); - return MEMCACHED_SUCCESS; -} - -static memcached_return_t network_connect(memcached_server_st *ptr) -{ - if (ptr->fd == -1) - { - struct addrinfo *use; - - if (!ptr->sockaddr_inited || - (!(ptr->root->flags.use_cache_lookups))) - { - memcached_return_t rc; - - rc= set_hostinfo(ptr); - if (rc != MEMCACHED_SUCCESS) - return rc; - ptr->sockaddr_inited= true; - } - - use= ptr->address_info; - /* Create the socket */ - while (use != NULL) - { - /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */ - if (ptr->type == MEMCACHED_CONNECTION_UDP && use->ai_family != AF_INET) - { - use= use->ai_next; - continue; - } - - if ((ptr->fd= socket(use->ai_family, - use->ai_socktype, - use->ai_protocol)) < 0) - { - ptr->cached_errno= errno; - WATCHPOINT_ERRNO(errno); - return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; - } - - (void)set_socket_options(ptr); - - /* connect to server */ - while (ptr->fd != -1 && - connect(ptr->fd, use->ai_addr, use->ai_addrlen) < 0) - { - ptr->cached_errno= errno; - if (errno == EINPROGRESS || /* nonblocking mode - first return, */ - errno == EALREADY) /* nonblocking mode - subsequent returns */ - { - struct pollfd fds[1]; - fds[0].fd = ptr->fd; - fds[0].events = POLLOUT; - int error= poll(fds, 1, ptr->root->connect_timeout); - - if (error != 1 || fds[0].revents & POLLERR) - { - if (fds[0].revents & POLLERR) - { - int err; - socklen_t len = sizeof (err); - (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); - ptr->cached_errno= (err == 0) ? errno : err; - } - - (void)close(ptr->fd); - ptr->fd= -1; - } - } - else if (errno == EISCONN) /* we are connected :-) */ - { - break; - } - else if (errno != EINTR) - { - (void)close(ptr->fd); - ptr->fd= -1; - break; - } - } - - if (ptr->fd != -1) - { - WATCHPOINT_ASSERT(ptr->cursor_active == 0); - ptr->server_failure_counter= 0; - return MEMCACHED_SUCCESS; - } - use = use->ai_next; - } - } - - if (ptr->fd == -1) - { - /* Failed to connect. schedule next retry */ - if (ptr->root->retry_timeout) - { - struct timeval next_time; - - if (gettimeofday(&next_time, NULL) == 0) - ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout; - } - ptr->server_failure_counter++; - if (ptr->cached_errno == 0) - return MEMCACHED_TIMEOUT; - - return MEMCACHED_ERRNO; /* The last error should be from connect() */ - } - - ptr->server_failure_counter= 0; - return MEMCACHED_SUCCESS; /* The last error should be from connect() */ -} - - -memcached_return_t memcached_connect(memcached_server_st *ptr) -{ - memcached_return_t rc= MEMCACHED_NO_SERVERS; - LIBMEMCACHED_MEMCACHED_CONNECT_START(); - - /* both retry_timeout and server_failure_limit must be set in order to delay retrying a server on error. */ - WATCHPOINT_ASSERT(ptr->root); - if (ptr->root->retry_timeout && ptr->root->server_failure_limit) - { - struct timeval curr_time; - - gettimeofday(&curr_time, NULL); - - /* if we've had too many consecutive errors on this server, mark it dead. */ - if (ptr->server_failure_counter >= ptr->root->server_failure_limit) - { - ptr->next_retry= curr_time.tv_sec + ptr->root->retry_timeout; - ptr->server_failure_counter= 0; - } - - if (curr_time.tv_sec < ptr->next_retry) - { - if (memcached_behavior_get(ptr->root, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS)) - run_distribution(ptr->root); - - ptr->root->last_disconnected_server = ptr; - return MEMCACHED_SERVER_MARKED_DEAD; - } - } - - /* We need to clean up the multi startup piece */ - switch (ptr->type) - { - case MEMCACHED_CONNECTION_UNKNOWN: - WATCHPOINT_ASSERT(0); - rc= MEMCACHED_NOT_SUPPORTED; - break; - case MEMCACHED_CONNECTION_UDP: - case MEMCACHED_CONNECTION_TCP: - rc= network_connect(ptr); - break; - case MEMCACHED_CONNECTION_UNIX_SOCKET: - rc= unix_socket_connect(ptr); - break; - default: - WATCHPOINT_ASSERT(0); - } - - unlikely ( rc != MEMCACHED_SUCCESS) ptr->root->last_disconnected_server = ptr; - - LIBMEMCACHED_MEMCACHED_CONNECT_END(); - - return rc; -} diff --git a/libmemcached/memcached_constants.h b/libmemcached/memcached_constants.h deleted file mode 100644 index cbdbf4b5..00000000 --- a/libmemcached/memcached_constants.h +++ /dev/null @@ -1,154 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Constants for libmemcached - * - */ - -#ifndef __MEMCACHED_CONSTANTS_H__ -#define __MEMCACHED_CONSTANTS_H__ - -/* Public defines */ -#define MEMCACHED_DEFAULT_PORT 11211 -#define MEMCACHED_MAX_KEY 251 /* We add one to have it null terminated */ -#define MEMCACHED_MAX_BUFFER 8196 -#define MEMCACHED_MAX_HOST_LENGTH 64 -#define MEMCACHED_MAX_HOST_SORT_LENGTH 86 /* Used for Ketama */ -#define MEMCACHED_POINTS_PER_SERVER 100 -#define MEMCACHED_POINTS_PER_SERVER_KETAMA 160 -#define MEMCACHED_CONTINUUM_SIZE MEMCACHED_POINTS_PER_SERVER*100 /* This would then set max hosts to 100 */ -#define MEMCACHED_STRIDE 4 -#define MEMCACHED_DEFAULT_TIMEOUT 1000 -#define MEMCACHED_CONTINUUM_ADDITION 10 /* How many extra slots we should build for in the continuum */ -#define MEMCACHED_PREFIX_KEY_MAX_SIZE 128 -#define MEMCACHED_EXPIRATION_NOT_ADD 0xffffffffU - -typedef enum { - MEMCACHED_SUCCESS, - MEMCACHED_FAILURE, - MEMCACHED_HOST_LOOKUP_FAILURE, - MEMCACHED_CONNECTION_FAILURE, - MEMCACHED_CONNECTION_BIND_FAILURE, - MEMCACHED_WRITE_FAILURE, - MEMCACHED_READ_FAILURE, - MEMCACHED_UNKNOWN_READ_FAILURE, - MEMCACHED_PROTOCOL_ERROR, - MEMCACHED_CLIENT_ERROR, - MEMCACHED_SERVER_ERROR, - MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE, - MEMCACHED_DATA_EXISTS, - MEMCACHED_DATA_DOES_NOT_EXIST, - MEMCACHED_NOTSTORED, - MEMCACHED_STORED, - MEMCACHED_NOTFOUND, - MEMCACHED_MEMORY_ALLOCATION_FAILURE, - MEMCACHED_PARTIAL_READ, - MEMCACHED_SOME_ERRORS, - MEMCACHED_NO_SERVERS, - MEMCACHED_END, - MEMCACHED_DELETED, - MEMCACHED_VALUE, - MEMCACHED_STAT, - MEMCACHED_ITEM, - MEMCACHED_ERRNO, - MEMCACHED_FAIL_UNIX_SOCKET, - MEMCACHED_NOT_SUPPORTED, - MEMCACHED_NO_KEY_PROVIDED, /* Deprecated. Use MEMCACHED_BAD_KEY_PROVIDED! */ - MEMCACHED_FETCH_NOTFINISHED, - MEMCACHED_TIMEOUT, - MEMCACHED_BUFFERED, - MEMCACHED_BAD_KEY_PROVIDED, - MEMCACHED_INVALID_HOST_PROTOCOL, - MEMCACHED_SERVER_MARKED_DEAD, - MEMCACHED_UNKNOWN_STAT_KEY, - MEMCACHED_E2BIG, - MEMCACHED_INVALID_ARGUMENTS, - MEMCACHED_MAXIMUM_RETURN /* Always add new error code before */ -} memcached_return_t; - - -typedef enum { - MEMCACHED_DISTRIBUTION_MODULA, - MEMCACHED_DISTRIBUTION_CONSISTENT, - MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA, - MEMCACHED_DISTRIBUTION_RANDOM, - MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY -} memcached_server_distribution_t; - -typedef enum { - MEMCACHED_BEHAVIOR_NO_BLOCK, - MEMCACHED_BEHAVIOR_TCP_NODELAY, - MEMCACHED_BEHAVIOR_HASH, - MEMCACHED_BEHAVIOR_KETAMA, - MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE, - MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE, - MEMCACHED_BEHAVIOR_CACHE_LOOKUPS, - MEMCACHED_BEHAVIOR_SUPPORT_CAS, - MEMCACHED_BEHAVIOR_POLL_TIMEOUT, - MEMCACHED_BEHAVIOR_DISTRIBUTION, - MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, - MEMCACHED_BEHAVIOR_USER_DATA, - MEMCACHED_BEHAVIOR_SORT_HOSTS, - MEMCACHED_BEHAVIOR_VERIFY_KEY, - MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, - MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, - MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, - MEMCACHED_BEHAVIOR_KETAMA_HASH, - MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, - MEMCACHED_BEHAVIOR_SND_TIMEOUT, - MEMCACHED_BEHAVIOR_RCV_TIMEOUT, - MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, - MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK, - MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK, - MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH, - MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY, - MEMCACHED_BEHAVIOR_NOREPLY, - MEMCACHED_BEHAVIOR_USE_UDP, - MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS, - MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, - MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE, - MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ -} memcached_behavior_t; - -#define MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED 0 -#define MEMCACHED_KETAMA_COMPAT_SPY 1 - -typedef enum { - MEMCACHED_CALLBACK_PREFIX_KEY = 0, - MEMCACHED_CALLBACK_USER_DATA = 1, - MEMCACHED_CALLBACK_CLEANUP_FUNCTION = 2, - MEMCACHED_CALLBACK_CLONE_FUNCTION = 3, -#ifdef MEMCACHED_ENABLE_DEPRECATED - MEMCACHED_CALLBACK_MALLOC_FUNCTION = 4, - MEMCACHED_CALLBACK_REALLOC_FUNCTION = 5, - MEMCACHED_CALLBACK_FREE_FUNCTION = 6, -#endif - MEMCACHED_CALLBACK_GET_FAILURE = 7, - MEMCACHED_CALLBACK_DELETE_TRIGGER = 8 -} memcached_callback_t; - -typedef enum { - MEMCACHED_HASH_DEFAULT= 0, - MEMCACHED_HASH_MD5, - MEMCACHED_HASH_CRC, - MEMCACHED_HASH_FNV1_64, - MEMCACHED_HASH_FNV1A_64, - MEMCACHED_HASH_FNV1_32, - MEMCACHED_HASH_FNV1A_32, - MEMCACHED_HASH_HSIEH, - MEMCACHED_HASH_MURMUR, - MEMCACHED_HASH_JENKINS -} memcached_hash_t; - -typedef enum { - MEMCACHED_CONNECTION_UNKNOWN, - MEMCACHED_CONNECTION_TCP, - MEMCACHED_CONNECTION_UDP, - MEMCACHED_CONNECTION_UNIX_SOCKET -} memcached_connection_t; - -#endif /* __MEMCACHED_CONSTANTS_H__ */ diff --git a/libmemcached/memcached_delete.c b/libmemcached/memcached_delete.c deleted file mode 100644 index bbdd2527..00000000 --- a/libmemcached/memcached_delete.c +++ /dev/null @@ -1,185 +0,0 @@ -#include "common.h" -#include "memcached/protocol_binary.h" - -memcached_return_t memcached_delete(memcached_st *ptr, const char *key, size_t key_length, - time_t expiration) -{ - return memcached_delete_by_key(ptr, key, key_length, - key, key_length, expiration); -} - -static inline memcached_return_t binary_delete(memcached_st *ptr, - unsigned int server_key, - const char *key, - size_t key_length, - uint8_t flush); - -memcached_return_t memcached_delete_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - time_t expiration) -{ - uint8_t to_write; - size_t send_length; - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - unsigned int server_key; - - LIBMEMCACHED_MEMCACHED_DELETE_START(); - - rc= memcached_validate_key_length(key_length, - ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - unlikely (ptr->hosts == NULL || ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - server_key= memcached_generate_hash(ptr, master_key, master_key_length); - to_write= (uint8_t)((ptr->flags.buffer_requests) ? 0 : 1); - bool no_reply= (ptr->flags.no_reply); - - if (ptr->flags.binary_protocol) - { - likely (!expiration) - rc= binary_delete(ptr, server_key, key, key_length, to_write); - else - rc= MEMCACHED_INVALID_ARGUMENTS; - } - else - { - unlikely (expiration) - { - if ((ptr->hosts[server_key].major_version == 1 && - ptr->hosts[server_key].minor_version > 2) || - ptr->hosts[server_key].major_version > 1) - { - rc= MEMCACHED_INVALID_ARGUMENTS; - goto error; - } - else - { - if (ptr->hosts[server_key].minor_version == 0) - { - if (no_reply || !to_write) - { - /* We might get out of sync with the server if we - * send this command to a server newer than 1.2.x.. - * disable no_reply and buffered mode. - */ - to_write= 1; - if (no_reply) - memcached_server_response_increment(&ptr->hosts[server_key]); - no_reply= false; - } - } - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "delete %s%.*s %u%s\r\n", - ptr->prefix_key, - (int) key_length, key, - (uint32_t)expiration, - no_reply ? " noreply" :"" ); - } - } - else - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "delete %s%.*s%s\r\n", - ptr->prefix_key, - (int)key_length, key, no_reply ? " noreply" :""); - - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) - { - rc= MEMCACHED_WRITE_FAILURE; - goto error; - } - - if (ptr->flags.use_udp && !to_write) - { - if (send_length > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - return MEMCACHED_WRITE_FAILURE; - if (send_length + ptr->hosts[server_key].write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - memcached_io_write(&ptr->hosts[server_key], NULL, 0, 1); - } - - rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, to_write); - } - - if (rc != MEMCACHED_SUCCESS) - goto error; - - if (!to_write) - rc= MEMCACHED_BUFFERED; - else if (!no_reply) - { - rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - if (rc == MEMCACHED_DELETED) - rc= MEMCACHED_SUCCESS; - } - - if (rc == MEMCACHED_SUCCESS && ptr->delete_trigger) - ptr->delete_trigger(ptr, key, key_length); - -error: - LIBMEMCACHED_MEMCACHED_DELETE_END(); - return rc; -} - -static inline memcached_return_t binary_delete(memcached_st *ptr, - unsigned int server_key, - const char *key, - size_t key_length, - uint8_t flush) -{ - protocol_binary_request_delete request= {.bytes= {0}}; - - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - if (ptr->flags.no_reply) - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; - else - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETE; - request.message.header.request.keylen= htons((uint16_t)key_length); - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl((uint32_t) key_length); - - if (ptr->flags.use_udp && !flush) - { - size_t cmd_size= sizeof(request.bytes) + key_length; - if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - return MEMCACHED_WRITE_FAILURE; - if (cmd_size + ptr->hosts[server_key].write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - memcached_io_write(&ptr->hosts[server_key], NULL, 0, 1); - } - - memcached_return_t rc= MEMCACHED_SUCCESS; - - if ((memcached_do(&ptr->hosts[server_key], request.bytes, - sizeof(request.bytes), 0) != MEMCACHED_SUCCESS) || - (memcached_io_write(&ptr->hosts[server_key], key, - key_length, (char) flush) == -1)) - { - memcached_io_reset(&ptr->hosts[server_key]); - rc= MEMCACHED_WRITE_FAILURE; - } - - unlikely (ptr->number_of_replicas > 0) - { - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_DELETEQ; - - for (uint32_t x= 0; x < ptr->number_of_replicas; ++x) - { - ++server_key; - if (server_key == ptr->number_of_hosts) - server_key= 0; - - memcached_server_st* server= &ptr->hosts[server_key]; - if ((memcached_do(server, (const char*)request.bytes, - sizeof(request.bytes), 0) != MEMCACHED_SUCCESS) || - (memcached_io_write(server, key, key_length, (char) flush) == -1)) - memcached_io_reset(server); - else - memcached_server_response_decrement(server); - } - } - - return rc; -} diff --git a/libmemcached/memcached_do.c b/libmemcached/memcached_do.c deleted file mode 100644 index d673f187..00000000 --- a/libmemcached/memcached_do.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "common.h" - -memcached_return_t memcached_do(memcached_server_st *ptr, const void *command, - size_t command_length, uint8_t with_flush) -{ - memcached_return_t rc; - ssize_t sent_length; - - WATCHPOINT_ASSERT(command_length); - WATCHPOINT_ASSERT(command); - - if ((rc= memcached_connect(ptr)) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(rc); - return rc; - } - - /* - ** Since non buffering ops in UDP mode dont check to make sure they will fit - ** before they start writing, if there is any data in buffer, clear it out, - ** otherwise we might get a partial write. - **/ - if (ptr->type == MEMCACHED_CONNECTION_UDP && with_flush && ptr->write_buffer_offset > UDP_DATAGRAM_HEADER_LENGTH) - memcached_io_write(ptr, NULL, 0, 1); - - sent_length= memcached_io_write(ptr, command, command_length, (char) with_flush); - - if (sent_length == -1 || (size_t)sent_length != command_length) - rc= MEMCACHED_WRITE_FAILURE; - else if ((ptr->root->flags.no_reply) == 0) - memcached_server_response_increment(ptr); - - return rc; -} diff --git a/libmemcached/memcached_dump.c b/libmemcached/memcached_dump.c deleted file mode 100644 index 4c1006e7..00000000 --- a/libmemcached/memcached_dump.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - We use this to dump all keys. - - At this point we only support a callback method. This could be optimized by first - calling items and finding active slabs. For the moment though we just loop through - all slabs on servers and "grab" the keys. -*/ - -#include "common.h" -static memcached_return_t ascii_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) -{ - memcached_return_t rc= 0; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - size_t send_length; - uint32_t server_key; - uint32_t x; - - unlikely (ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - for (server_key= 0; server_key < ptr->number_of_hosts; server_key++) - { - /* 256 I BELIEVE is the upper limit of slabs */ - for (x= 0; x < 256; x++) - { - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "stats cachedump %u 0 0\r\n", x); - - rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, 1); - - unlikely (rc != MEMCACHED_SUCCESS) - goto error; - - while (1) - { - uint32_t callback_counter; - rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - if (rc == MEMCACHED_ITEM) - { - char *string_ptr, *end_ptr; - char *key; - - string_ptr= buffer; - string_ptr+= 5; /* Move past ITEM */ - for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); - key= string_ptr; - key[(size_t)(end_ptr-string_ptr)]= 0; - for (callback_counter= 0; callback_counter < number_of_callbacks; callback_counter++) - { - rc= (*callback[callback_counter])(ptr, key, (size_t)(end_ptr-string_ptr), context); - if (rc != MEMCACHED_SUCCESS) - break; - } - } - else if (rc == MEMCACHED_END) - break; - else if (rc == MEMCACHED_SERVER_ERROR || rc == MEMCACHED_CLIENT_ERROR) - { - /* If we try to request stats cachedump for a slab class that is too big - * the server will return an incorrect error message: - * "MEMCACHED_SERVER_ERROR failed to allocate memory" - * This isn't really a fatal error, so let's just skip it. I want to - * fix the return value from the memcached server to a CLIENT_ERROR, - * so let's add support for that as well right now. - */ - rc= MEMCACHED_END; - break; - } - else - goto error; - } - } - } - -error: - if (rc == MEMCACHED_END) - return MEMCACHED_SUCCESS; - else - return rc; -} - -memcached_return_t memcached_dump(memcached_st *ptr, memcached_dump_fn *callback, void *context, uint32_t number_of_callbacks) -{ - /* No support for Binary protocol yet */ - if (ptr->flags.binary_protocol) - return MEMCACHED_FAILURE; - - return ascii_dump(ptr, callback, context, number_of_callbacks); -} - diff --git a/libmemcached/memcached_fetch.c b/libmemcached/memcached_fetch.c deleted file mode 100644 index dbb1ff62..00000000 --- a/libmemcached/memcached_fetch.c +++ /dev/null @@ -1,106 +0,0 @@ -#include "common.h" -#include "memcached_io.h" - -char *memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, - size_t *value_length, - uint32_t *flags, - memcached_return_t *error) -{ - memcached_result_st *result_buffer= &ptr->result; - - unlikely (ptr->flags.use_udp) - { - *error= MEMCACHED_NOT_SUPPORTED; - return NULL; - } - - result_buffer= memcached_fetch_result(ptr, result_buffer, error); - - if (result_buffer == NULL || *error != MEMCACHED_SUCCESS) - { - WATCHPOINT_ASSERT(result_buffer == NULL); - *value_length= 0; - return NULL; - } - - *value_length= memcached_string_length(&result_buffer->value); - - if (key) - { - strncpy(key, result_buffer->key, result_buffer->key_length); - *key_length= result_buffer->key_length; - } - - if (result_buffer->flags) - *flags= result_buffer->flags; - else - *flags= 0; - - return memcached_string_c_copy(&result_buffer->value); -} - -memcached_result_st *memcached_fetch_result(memcached_st *ptr, - memcached_result_st *result, - memcached_return_t *error) -{ - memcached_server_st *server; - - unlikely (ptr->flags.use_udp) - { - *error= MEMCACHED_NOT_SUPPORTED; - return NULL; - } - - if (result == NULL) - if ((result= memcached_result_create(ptr, NULL)) == NULL) - return NULL; - - while ((server = memcached_io_get_readable_server(ptr)) != NULL) - { - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - *error= memcached_response(server, buffer, sizeof(buffer), result); - - if (*error == MEMCACHED_SUCCESS) - return result; - else if (*error == MEMCACHED_END) - memcached_server_response_reset(server); - else if (*error != MEMCACHED_NOTFOUND) - break; - } - - /* We have completed reading data */ - if (memcached_is_allocated(result)) - { - memcached_result_free(result); - } - else - { - memcached_string_reset(&result->value); - } - - return NULL; -} - -memcached_return_t memcached_fetch_execute(memcached_st *ptr, - memcached_execute_fn *callback, - void *context, - uint32_t number_of_callbacks) -{ - memcached_result_st *result= &ptr->result; - memcached_return_t rc= MEMCACHED_FAILURE; - unsigned int x; - - while ((result= memcached_fetch_result(ptr, result, &rc)) != NULL) - { - if (rc == MEMCACHED_SUCCESS) - { - for (x= 0; x < number_of_callbacks; x++) - { - rc= (*callback[x])(ptr, result, context); - if (rc != MEMCACHED_SUCCESS) - break; - } - } - } - return rc; -} diff --git a/libmemcached/memcached_flush.c b/libmemcached/memcached_flush.c deleted file mode 100644 index d05d7442..00000000 --- a/libmemcached/memcached_flush.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "common.h" - -static memcached_return_t memcached_flush_binary(memcached_st *ptr, - time_t expiration); -static memcached_return_t memcached_flush_textual(memcached_st *ptr, - time_t expiration); - -memcached_return_t memcached_flush(memcached_st *ptr, time_t expiration) -{ - memcached_return_t rc; - - LIBMEMCACHED_MEMCACHED_FLUSH_START(); - if (ptr->flags.binary_protocol) - rc= memcached_flush_binary(ptr, expiration); - else - rc= memcached_flush_textual(ptr, expiration); - LIBMEMCACHED_MEMCACHED_FLUSH_END(); - return rc; -} - -static memcached_return_t memcached_flush_textual(memcached_st *ptr, - time_t expiration) -{ - unsigned int x; - size_t send_length; - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - - unlikely (ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - for (x= 0; x < ptr->number_of_hosts; x++) - { - bool no_reply= ptr->flags.no_reply; - - if (expiration) - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "flush_all %llu%s\r\n", - (unsigned long long)expiration, no_reply ? " noreply" : ""); - else - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "flush_all%s\r\n", no_reply ? " noreply" : ""); - - rc= memcached_do(&ptr->hosts[x], buffer, send_length, 1); - - if (rc == MEMCACHED_SUCCESS && !no_reply) - (void)memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - } - - return MEMCACHED_SUCCESS; -} - -static memcached_return_t memcached_flush_binary(memcached_st *ptr, - time_t expiration) -{ - unsigned int x; - protocol_binary_request_flush request= {.bytes= {0}}; - - unlikely (ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - request.message.header.request.magic= (uint8_t)PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; - request.message.header.request.extlen= 4; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl(request.message.header.request.extlen); - request.message.body.expiration= htonl((uint32_t) expiration); - - for (x= 0; x < ptr->number_of_hosts; x++) - { - if (ptr->flags.no_reply) - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSHQ; - else - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_FLUSH; - if (memcached_do(&ptr->hosts[x], request.bytes, - sizeof(request.bytes), 1) != MEMCACHED_SUCCESS) - { - memcached_io_reset(&ptr->hosts[x]); - return MEMCACHED_WRITE_FAILURE; - } - } - - for (x= 0; x < ptr->number_of_hosts; x++) - { - if (memcached_server_response_count(&ptr->hosts[x]) > 0) - (void)memcached_response(&ptr->hosts[x], NULL, 0, NULL); - } - - return MEMCACHED_SUCCESS; -} diff --git a/libmemcached/memcached_flush_buffers.c b/libmemcached/memcached_flush_buffers.c deleted file mode 100644 index f001e21e..00000000 --- a/libmemcached/memcached_flush_buffers.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "common.h" -#include "memcached_io.h" - -memcached_return_t memcached_flush_buffers(memcached_st *mem) -{ - memcached_return_t ret= MEMCACHED_SUCCESS; - - for (uint32_t x= 0; x < mem->number_of_hosts; ++x) - if (mem->hosts[x].write_buffer_offset != 0) - { - if (mem->hosts[x].fd == -1 && - (ret= memcached_connect(&mem->hosts[x])) != MEMCACHED_SUCCESS) - { - WATCHPOINT_ERROR(ret); - return ret; - } - if (memcached_io_write(&mem->hosts[x], NULL, 0, 1) == -1) - ret= MEMCACHED_SOME_ERRORS; - } - - return ret; -} diff --git a/libmemcached/memcached_get.c b/libmemcached/memcached_get.c deleted file mode 100644 index 27a1410e..00000000 --- a/libmemcached/memcached_get.c +++ /dev/null @@ -1,575 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Get functions for libmemcached - * - */ - -#include "common.h" -#include "memcached_io.h" - -/* - What happens if no servers exist? -*/ -char *memcached_get(memcached_st *ptr, const char *key, - size_t key_length, - size_t *value_length, - uint32_t *flags, - memcached_return_t *error) -{ - return memcached_get_by_key(ptr, NULL, 0, key, key_length, value_length, - flags, error); -} - -static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode); - -char *memcached_get_by_key(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char *key, size_t key_length, - size_t *value_length, - uint32_t *flags, - memcached_return_t *error) -{ - char *value; - size_t dummy_length; - uint32_t dummy_flags; - memcached_return_t dummy_error; - - unlikely (ptr->flags.use_udp) - { - *error= MEMCACHED_NOT_SUPPORTED; - return NULL; - } - - /* Request the key */ - *error= memcached_mget_by_key_real(ptr, master_key, master_key_length, - (const char * const *)&key, - &key_length, 1, false); - - value= memcached_fetch(ptr, NULL, NULL, - value_length, flags, error); - /* This is for historical reasons */ - if (*error == MEMCACHED_END) - *error= MEMCACHED_NOTFOUND; - - if (value == NULL) - { - if (ptr->get_key_failure && *error == MEMCACHED_NOTFOUND) - { - memcached_return_t rc; - - memcached_result_reset(&ptr->result); - rc= ptr->get_key_failure(ptr, key, key_length, &ptr->result); - - /* On all failure drop to returning NULL */ - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) - { - if (rc == MEMCACHED_BUFFERED) - { - uint64_t latch; /* We use latch to track the state of the original socket */ - latch= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS); - if (latch == 0) - memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1); - - rc= memcached_set(ptr, key, key_length, - memcached_result_value(&ptr->result), - memcached_result_length(&ptr->result), - 0, memcached_result_flags(&ptr->result)); - - if (rc == MEMCACHED_BUFFERED && latch == 0) - memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0); - } - else - { - rc= memcached_set(ptr, key, key_length, - memcached_result_value(&ptr->result), - memcached_result_length(&ptr->result), - 0, memcached_result_flags(&ptr->result)); - } - - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED) - { - *error= rc; - *value_length= memcached_result_length(&ptr->result); - *flags= memcached_result_flags(&ptr->result); - return memcached_string_c_copy(&ptr->result.value); - } - } - } - - return NULL; - } - - (void)memcached_fetch(ptr, NULL, NULL, - &dummy_length, &dummy_flags, - &dummy_error); - WATCHPOINT_ASSERT(dummy_length == 0); - - return value; -} - -memcached_return_t memcached_mget(memcached_st *ptr, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - return memcached_mget_by_key(ptr, NULL, 0, keys, key_length, number_of_keys); -} - -static memcached_return_t binary_mget_by_key(memcached_st *ptr, - unsigned int master_server_key, - bool is_master_key_set, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode); - -static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode) -{ - unsigned int x; - memcached_return_t rc= MEMCACHED_NOTFOUND; - const char *get_command= "get "; - uint8_t get_command_length= 4; - unsigned int master_server_key= (unsigned int)-1; /* 0 is a valid server id! */ - bool is_master_key_set= false; - - unlikely (ptr->flags.use_udp) - return MEMCACHED_NOT_SUPPORTED; - - LIBMEMCACHED_MEMCACHED_MGET_START(); - ptr->cursor_server= 0; - - if (number_of_keys == 0) - return MEMCACHED_NOTFOUND; - - if (ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - if (ptr->flags.verify_key && (memcached_key_test(keys, key_length, number_of_keys) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - - if (master_key && master_key_length) - { - if (ptr->flags.verify_key && (memcached_key_test((const char * const *)&master_key, &master_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - master_server_key= memcached_generate_hash(ptr, master_key, master_key_length); - is_master_key_set= true; - } - - /* - Here is where we pay for the non-block API. We need to remove any data sitting - in the queue before we start our get. - - It might be optimum to bounce the connection if count > some number. - */ - for (x= 0; x < ptr->number_of_hosts; x++) - { - if (memcached_server_response_count(&ptr->hosts[x])) - { - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - - if (ptr->flags.no_block) - (void)memcached_io_write(&ptr->hosts[x], NULL, 0, 1); - - while(memcached_server_response_count(&ptr->hosts[x])) - (void)memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, &ptr->result); - } - } - - if (ptr->flags.binary_protocol) - return binary_mget_by_key(ptr, master_server_key, is_master_key_set, keys, - key_length, number_of_keys, mget_mode); - - if (ptr->flags.support_cas) - { - get_command= "gets "; - get_command_length= 5; - } - - /* - If a server fails we warn about errors and start all over with sending keys - to the server. - */ - for (x= 0; x < number_of_keys; x++) - { - unsigned int server_key; - - if (is_master_key_set) - server_key= master_server_key; - else - server_key= memcached_generate_hash(ptr, keys[x], key_length[x]); - - if (memcached_server_response_count(&ptr->hosts[server_key]) == 0) - { - rc= memcached_connect(&ptr->hosts[server_key]); - - if (rc != MEMCACHED_SUCCESS) - continue; - - if ((memcached_io_write(&ptr->hosts[server_key], get_command, get_command_length, 0)) == -1) - { - rc= MEMCACHED_SOME_ERRORS; - continue; - } - WATCHPOINT_ASSERT(ptr->hosts[server_key].cursor_active == 0); - memcached_server_response_increment(&ptr->hosts[server_key]); - WATCHPOINT_ASSERT(ptr->hosts[server_key].cursor_active == 1); - } - - /* Only called when we have a prefix key */ - if (ptr->prefix_key[0] != 0) - { - if ((memcached_io_write(&ptr->hosts[server_key], ptr->prefix_key, ptr->prefix_key_length, 0)) == -1) - { - memcached_server_response_reset(&ptr->hosts[server_key]); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - } - - if ((memcached_io_write(&ptr->hosts[server_key], keys[x], key_length[x], 0)) == -1) - { - memcached_server_response_reset(&ptr->hosts[server_key]); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - if ((memcached_io_write(&ptr->hosts[server_key], " ", 1, 0)) == -1) - { - memcached_server_response_reset(&ptr->hosts[server_key]); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - } - - /* - Should we muddle on if some servers are dead? - */ - for (x= 0; x < ptr->number_of_hosts; x++) - { - if (memcached_server_response_count(&ptr->hosts[x])) - { - /* We need to do something about non-connnected hosts in the future */ - if ((memcached_io_write(&ptr->hosts[x], "\r\n", 2, 1)) == -1) - { - rc= MEMCACHED_SOME_ERRORS; - } - } - } - - LIBMEMCACHED_MEMCACHED_MGET_END(); - return rc; -} - -memcached_return_t memcached_mget_by_key(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - return memcached_mget_by_key_real(ptr, master_key, master_key_length, keys, - key_length, number_of_keys, true); -} - -memcached_return_t memcached_mget_execute(memcached_st *ptr, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - memcached_execute_fn *callback, - void *context, - unsigned int number_of_callbacks) -{ - return memcached_mget_execute_by_key(ptr, NULL, 0, keys, key_length, - number_of_keys, callback, - context, number_of_callbacks); -} - -memcached_return_t memcached_mget_execute_by_key(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - memcached_execute_fn *callback, - void *context, - unsigned int number_of_callbacks) -{ - if ((ptr->flags.binary_protocol) == 0) - return MEMCACHED_NOT_SUPPORTED; - - memcached_return_t rc; - memcached_callback_st *original_callbacks= ptr->callbacks; - memcached_callback_st cb= { - .callback= callback, - .context= context, - .number_of_callback= number_of_callbacks - }; - - ptr->callbacks= &cb; - rc= memcached_mget_by_key(ptr, master_key, master_key_length, keys, - key_length, number_of_keys); - ptr->callbacks= original_callbacks; - return rc; -} - -static memcached_return_t simple_binary_mget(memcached_st *ptr, - unsigned int master_server_key, - bool is_master_key_set, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, bool mget_mode) -{ - memcached_return_t rc= MEMCACHED_NOTFOUND; - uint32_t x; - - int flush= number_of_keys == 1; - - /* - If a server fails we warn about errors and start all over with sending keys - to the server. - */ - for (x= 0; x < number_of_keys; x++) - { - unsigned int server_key; - - if (is_master_key_set) - server_key= master_server_key; - else - server_key= memcached_generate_hash(ptr, keys[x], key_length[x]); - - if (memcached_server_response_count(&ptr->hosts[server_key]) == 0) - { - rc= memcached_connect(&ptr->hosts[server_key]); - if (rc != MEMCACHED_SUCCESS) - continue; - } - - protocol_binary_request_getk request= {.bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - if (mget_mode) - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ; - else - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK; - - memcached_return_t vk; - vk= memcached_validate_key_length(key_length[x], - ptr->flags.binary_protocol); - unlikely (vk != MEMCACHED_SUCCESS) - { - if (x > 0) - memcached_io_reset(&ptr->hosts[server_key]); - return vk; - } - - request.message.header.request.keylen= htons((uint16_t)key_length[x]); - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - request.message.header.request.bodylen= htonl((uint32_t) key_length[x]); - - if ((memcached_io_write(&ptr->hosts[server_key], request.bytes, - sizeof(request.bytes), 0) == -1) || - (memcached_io_write(&ptr->hosts[server_key], keys[x], - key_length[x], (char) flush) == -1)) - { - memcached_server_response_reset(&ptr->hosts[server_key]); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - /* We just want one pending response per server */ - memcached_server_response_reset(&ptr->hosts[server_key]); - memcached_server_response_increment(&ptr->hosts[server_key]); - if ((x > 0 && x == ptr->io_key_prefetch) && - memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS) - rc= MEMCACHED_SOME_ERRORS; - } - - if (mget_mode) - { - /* - * Send a noop command to flush the buffers - */ - protocol_binary_request_noop request= {.bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_NOOP; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - - for (x= 0; x < ptr->number_of_hosts; x++) - if (memcached_server_response_count(&ptr->hosts[x])) - { - if (memcached_io_write(&ptr->hosts[x], NULL, 0, 1) == -1) - { - memcached_server_response_reset(&ptr->hosts[x]); - memcached_io_reset(&ptr->hosts[x]); - rc= MEMCACHED_SOME_ERRORS; - } - - if (memcached_io_write(&ptr->hosts[x], request.bytes, - sizeof(request.bytes), 1) == -1) - { - memcached_server_response_reset(&ptr->hosts[x]); - memcached_io_reset(&ptr->hosts[x]); - rc= MEMCACHED_SOME_ERRORS; - } - } - } - - - return rc; -} - -static memcached_return_t replication_binary_mget(memcached_st *ptr, - uint32_t* hash, - bool* dead_servers, - const char *const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - memcached_return_t rc= MEMCACHED_NOTFOUND; - uint32_t x, start= 0; - uint64_t randomize_read= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ); - - if (randomize_read) - start= (uint32_t)random() % (uint32_t)(ptr->number_of_replicas + 1); - - /* Loop for each replica */ - for (uint32_t replica= 0; replica <= ptr->number_of_replicas; ++replica) - { - bool success= true; - - for (x= 0; x < number_of_keys; ++x) - { - if (hash[x] == ptr->number_of_hosts) - continue; /* Already successfully sent */ - - uint32_t server= hash[x] + replica; - - /* In case of randomized reads */ - if (randomize_read && ((server + start) <= (hash[x] + ptr->number_of_replicas))) - server += start; - - while (server >= ptr->number_of_hosts) - server -= ptr->number_of_hosts; - - if (dead_servers[server]) - continue; - - if (memcached_server_response_count(&ptr->hosts[server]) == 0) - { - rc= memcached_connect(&ptr->hosts[server]); - if (rc != MEMCACHED_SUCCESS) - { - memcached_io_reset(&ptr->hosts[server]); - dead_servers[server]= true; - success= false; - continue; - } - } - - protocol_binary_request_getk request= { - .message.header.request= { - .magic= PROTOCOL_BINARY_REQ, - .opcode= PROTOCOL_BINARY_CMD_GETK, - .keylen= htons((uint16_t)key_length[x]), - .datatype= PROTOCOL_BINARY_RAW_BYTES, - .bodylen= htonl((uint32_t)key_length[x]) - } - }; - - /* - * We need to disable buffering to actually know that the request was - * successfully sent to the server (so that we should expect a result - * back). It would be nice to do this in buffered mode, but then it - * would be complex to handle all error situations if we got to send - * some of the messages, and then we failed on writing out some others - * and we used the callback interface from memcached_mget_execute so - * that we might have processed some of the responses etc. For now, - * just make sure we work _correctly_ - */ - if ((memcached_io_write(&ptr->hosts[server], request.bytes, - sizeof(request.bytes), 0) == -1) || - (memcached_io_write(&ptr->hosts[server], keys[x], - key_length[x], 1) == -1)) - { - memcached_io_reset(&ptr->hosts[server]); - dead_servers[server]= true; - success= false; - continue; - } - - memcached_server_response_increment(&ptr->hosts[server]); - hash[x]= ptr->number_of_hosts; - } - - if (success) - break; - } - - return rc; -} - -static memcached_return_t binary_mget_by_key(memcached_st *ptr, - unsigned int master_server_key, - bool is_master_key_set, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - bool mget_mode) -{ - memcached_return_t rc; - - if (ptr->number_of_replicas == 0) - { - rc= simple_binary_mget(ptr, master_server_key, is_master_key_set, - keys, key_length, number_of_keys, mget_mode); - } - else - { - uint32_t* hash; - bool* dead_servers; - - hash= ptr->call_malloc(ptr, sizeof(uint32_t) * number_of_keys); - dead_servers= ptr->call_calloc(ptr, ptr->number_of_hosts, sizeof(bool)); - - if (hash == NULL || dead_servers == NULL) - { - ptr->call_free(ptr, hash); - ptr->call_free(ptr, dead_servers); - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - } - - if (is_master_key_set) - for (unsigned int x= 0; x < number_of_keys; x++) - hash[x]= master_server_key; - else - for (unsigned int x= 0; x < number_of_keys; x++) - hash[x]= memcached_generate_hash(ptr, keys[x], key_length[x]); - - rc= replication_binary_mget(ptr, hash, dead_servers, keys, - key_length, number_of_keys); - - ptr->call_free(ptr, hash); - ptr->call_free(ptr, dead_servers); - - return MEMCACHED_SUCCESS; - } - - return rc; -} diff --git a/libmemcached/memcached_get.h b/libmemcached/memcached_get.h deleted file mode 100644 index bb04a154..00000000 --- a/libmemcached/memcached_get.h +++ /dev/null @@ -1,84 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Get functions for libmemcached - * - */ - -#ifndef LIBMEMCACHED_MEMCACHED_GET_H -#define LIBMEMCACHED_MEMCACHED_GET_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Public defines */ -LIBMEMCACHED_API -char *memcached_get(memcached_st *ptr, - const char *key, size_t key_length, - size_t *value_length, - uint32_t *flags, - memcached_return_t *error); - -LIBMEMCACHED_API -memcached_return_t memcached_mget(memcached_st *ptr, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys); - -LIBMEMCACHED_API -char *memcached_get_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - size_t *value_length, - uint32_t *flags, - memcached_return_t *error); - -LIBMEMCACHED_API -memcached_return_t memcached_mget_by_key(memcached_st *ptr, - const char *master_key, size_t - master_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys); - -LIBMEMCACHED_API -char *memcached_fetch(memcached_st *ptr, - char *key, size_t *key_length, - size_t *value_length, uint32_t *flags, - memcached_return_t *error); - -LIBMEMCACHED_API -memcached_result_st *memcached_fetch_result(memcached_st *ptr, - memcached_result_st *result, - memcached_return_t *error); - -LIBMEMCACHED_API -memcached_return_t memcached_mget_execute(memcached_st *ptr, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - memcached_execute_fn *callback, - void *context, - unsigned int number_of_callbacks); - -LIBMEMCACHED_API -memcached_return_t memcached_mget_execute_by_key(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char * const *keys, - const size_t *key_length, - size_t number_of_keys, - memcached_execute_fn *callback, - void *context, - unsigned int number_of_callbacks); - -#ifdef __cplusplus -} -#endif - -#endif /* LIBMEMCACHED_MEMCACHED_GET_H */ diff --git a/libmemcached/memcached_hash.c b/libmemcached/memcached_hash.c deleted file mode 100644 index 395020a3..00000000 --- a/libmemcached/memcached_hash.c +++ /dev/null @@ -1,234 +0,0 @@ -#include "common.h" - - -/* Defines */ -static uint64_t FNV_64_INIT= UINT64_C(0xcbf29ce484222325); -static uint64_t FNV_64_PRIME= UINT64_C(0x100000001b3); - -static uint32_t FNV_32_INIT= 2166136261UL; -static uint32_t FNV_32_PRIME= 16777619; - -/* Prototypes */ -static uint32_t internal_generate_hash(const char *key, size_t key_length); -static uint32_t internal_generate_md5(const char *key, size_t key_length); - -uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memcached_hash_t hash_algorithm) -{ - uint32_t hash= 1; /* Just here to remove compile warning */ - uint32_t x= 0; - - switch (hash_algorithm) - { - case MEMCACHED_HASH_DEFAULT: - hash= internal_generate_hash(key, key_length); - break; - case MEMCACHED_HASH_MD5: - hash= internal_generate_md5(key, key_length); - break; - case MEMCACHED_HASH_CRC: - hash= ((hash_crc32(key, key_length) >> 16) & 0x7fff); - if (hash == 0) - hash= 1; - break; - /* FNV hash'es lifted from Dustin Sallings work */ - case MEMCACHED_HASH_FNV1_64: - { - /* Thanks to pierre@demartines.com for the pointer */ - uint64_t temp_hash; - - temp_hash= FNV_64_INIT; - for (x= 0; x < key_length; x++) - { - temp_hash *= FNV_64_PRIME; - temp_hash ^= (uint64_t)key[x]; - } - hash= (uint32_t)temp_hash; - } - break; - case MEMCACHED_HASH_FNV1A_64: - { - hash= (uint32_t) FNV_64_INIT; - for (x= 0; x < key_length; x++) - { - uint32_t val= (uint32_t)key[x]; - hash ^= val; - hash *= (uint32_t) FNV_64_PRIME; - } - } - break; - case MEMCACHED_HASH_FNV1_32: - { - hash= FNV_32_INIT; - for (x= 0; x < key_length; x++) - { - uint32_t val= (uint32_t)key[x]; - hash *= FNV_32_PRIME; - hash ^= val; - } - } - break; - case MEMCACHED_HASH_FNV1A_32: - { - hash= FNV_32_INIT; - for (x= 0; x < key_length; x++) - { - uint32_t val= (uint32_t)key[x]; - hash ^= val; - hash *= FNV_32_PRIME; - } - } - break; - case MEMCACHED_HASH_HSIEH: - { -#ifdef HAVE_HSIEH_HASH - hash= hsieh_hash(key, key_length); -#endif - break; - } - case MEMCACHED_HASH_MURMUR: - { - hash= murmur_hash(key, key_length); - break; - } - case MEMCACHED_HASH_JENKINS: - { - hash=jenkins_hash(key, key_length, 13); - break; - } - default: - { - WATCHPOINT_ASSERT(hash_algorithm); - break; - } - } - return hash; -} - -uint32_t generate_hash(memcached_st *ptr, const char *key, size_t key_length) -{ - uint32_t hash= 1; /* Just here to remove compile warning */ - - - WATCHPOINT_ASSERT(ptr->number_of_hosts); - - if (ptr->number_of_hosts == 1) - return 0; - - hash= memcached_generate_hash_value(key, key_length, ptr->hash); - WATCHPOINT_ASSERT(hash); - return hash; -} - -static uint32_t dispatch_host(memcached_st *ptr, uint32_t hash) -{ - switch (ptr->distribution) - { - case MEMCACHED_DISTRIBUTION_CONSISTENT: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: - { - uint32_t num= ptr->continuum_points_counter; - WATCHPOINT_ASSERT(ptr->continuum); - - hash= hash; - memcached_continuum_item_st *begin, *end, *left, *right, *middle; - begin= left= ptr->continuum; - end= right= ptr->continuum + num; - - while (left < right) - { - middle= left + (right - left) / 2; - if (middle->value < hash) - left= middle + 1; - else - right= middle; - } - if (right == end) - right= begin; - return right->index; - } - case MEMCACHED_DISTRIBUTION_MODULA: - return hash % ptr->number_of_hosts; - case MEMCACHED_DISTRIBUTION_RANDOM: - return (uint32_t) random() % ptr->number_of_hosts; - default: - WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ - return hash % ptr->number_of_hosts; - } - - /* NOTREACHED */ -} - -/* - One day make this public, and have it return the actual memcached_server_st - to the calling application. -*/ -uint32_t memcached_generate_hash(memcached_st *ptr, const char *key, size_t key_length) -{ - uint32_t hash= 1; /* Just here to remove compile warning */ - - WATCHPOINT_ASSERT(ptr->number_of_hosts); - - if (ptr->number_of_hosts == 1) - return 0; - - if (ptr->flags.hash_with_prefix_key) - { - size_t temp_length= ptr->prefix_key_length + key_length; - char temp[temp_length]; - - if (temp_length > MEMCACHED_MAX_KEY -1) - return 0; - - strncpy(temp, ptr->prefix_key, ptr->prefix_key_length); - strncpy(temp + ptr->prefix_key_length, key, key_length); - hash= generate_hash(ptr, temp, temp_length); - } - else - { - hash= generate_hash(ptr, key, key_length); - } - - WATCHPOINT_ASSERT(hash); - - if (memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS) && ptr->next_distribution_rebuild) { - struct timeval now; - - if (gettimeofday(&now, NULL) == 0 && - now.tv_sec > ptr->next_distribution_rebuild) - run_distribution(ptr); - } - - return dispatch_host(ptr, hash); -} - -static uint32_t internal_generate_hash(const char *key, size_t key_length) -{ - const char *ptr= key; - uint32_t value= 0; - - while (key_length--) - { - uint32_t val= (uint32_t) *ptr++; - value += val; - value += (value << 10); - value ^= (value >> 6); - } - value += (value << 3); - value ^= (value >> 11); - value += (value << 15); - - return value == 0 ? 1 : (uint32_t) value; -} - -static uint32_t internal_generate_md5(const char *key, size_t key_length) -{ - unsigned char results[16]; - - md5_signature((unsigned char*)key, (unsigned int)key_length, results); - - return ((uint32_t) (results[3] & 0xFF) << 24) - | ((uint32_t) (results[2] & 0xFF) << 16) - | ((uint32_t) (results[1] & 0xFF) << 8) - | (results[0] & 0xFF); -} diff --git a/libmemcached/memcached_hosts.c b/libmemcached/memcached_hosts.c deleted file mode 100644 index 8cc5f3e6..00000000 --- a/libmemcached/memcached_hosts.c +++ /dev/null @@ -1,514 +0,0 @@ -#include "common.h" -#include - -/* Protoypes (static) */ -static memcached_return_t server_add(memcached_st *ptr, const char *hostname, - in_port_t port, - uint32_t weight, - memcached_connection_t type); -memcached_return_t update_continuum(memcached_st *ptr); - -static int compare_servers(const void *p1, const void *p2) -{ - int return_value; - memcached_server_st *a= (memcached_server_st *)p1; - memcached_server_st *b= (memcached_server_st *)p2; - - return_value= strcmp(a->hostname, b->hostname); - - if (return_value == 0) - { - return_value= (int) (a->port - b->port); - } - - return return_value; -} - -static void sort_hosts(memcached_st *ptr) -{ - if (ptr->number_of_hosts) - { - qsort(ptr->hosts, ptr->number_of_hosts, sizeof(memcached_server_st), compare_servers); - ptr->hosts[0].count= (uint16_t) ptr->number_of_hosts; - } -} - - -memcached_return_t run_distribution(memcached_st *ptr) -{ - switch (ptr->distribution) - { - case MEMCACHED_DISTRIBUTION_CONSISTENT: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA: - case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY: - return update_continuum(ptr); - case MEMCACHED_DISTRIBUTION_MODULA: - if (ptr->flags.use_sort_hosts) - sort_hosts(ptr); - break; - case MEMCACHED_DISTRIBUTION_RANDOM: - break; - default: - WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */ - } - - ptr->last_disconnected_server = NULL; - - return MEMCACHED_SUCCESS; -} - -void server_list_free(memcached_st *ptr, memcached_server_st *servers) -{ - unsigned int x; - - if (servers == NULL) - return; - - for (x= 0; x < servers->count; x++) - if (servers[x].address_info) - { - freeaddrinfo(servers[x].address_info); - servers[x].address_info= NULL; - } - - if (ptr) - ptr->call_free(ptr, servers); - else - free(servers); -} - -static uint32_t ketama_server_hash(const char *key, unsigned int key_length, int alignment) -{ - unsigned char results[16]; - - md5_signature((unsigned char*)key, key_length, results); - return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24) - | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16) - | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8) - | (results[0 + alignment * 4] & 0xFF); -} - -static int continuum_item_cmp(const void *t1, const void *t2) -{ - memcached_continuum_item_st *ct1= (memcached_continuum_item_st *)t1; - memcached_continuum_item_st *ct2= (memcached_continuum_item_st *)t2; - - /* Why 153? Hmmm... */ - WATCHPOINT_ASSERT(ct1->value != 153); - if (ct1->value == ct2->value) - return 0; - else if (ct1->value > ct2->value) - return 1; - else - return -1; -} - -memcached_return_t update_continuum(memcached_st *ptr) -{ - uint32_t host_index; - uint32_t continuum_index= 0; - uint32_t value; - memcached_server_st *list; - uint32_t pointer_index; - uint32_t pointer_counter= 0; - uint32_t pointer_per_server= MEMCACHED_POINTS_PER_SERVER; - uint32_t pointer_per_hash= 1; - uint64_t total_weight= 0; - uint64_t is_ketama_weighted= 0; - uint64_t is_auto_ejecting= 0; - uint32_t points_per_server= 0; - uint32_t live_servers= 0; - struct timeval now; - - if (gettimeofday(&now, NULL) != 0) - { - ptr->cached_errno = errno; - return MEMCACHED_ERRNO; - } - - list = ptr->hosts; - - /* count live servers (those without a retry delay set) */ - is_auto_ejecting= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS); - if (is_auto_ejecting) - { - live_servers= 0; - ptr->next_distribution_rebuild= 0; - for (host_index= 0; host_index < ptr->number_of_hosts; ++host_index) - { - if (list[host_index].next_retry <= now.tv_sec) - live_servers++; - else - { - if (ptr->next_distribution_rebuild == 0 || list[host_index].next_retry < ptr->next_distribution_rebuild) - ptr->next_distribution_rebuild= list[host_index].next_retry; - } - } - } - else - live_servers= ptr->number_of_hosts; - - is_ketama_weighted= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED); - points_per_server= (uint32_t) (is_ketama_weighted ? MEMCACHED_POINTS_PER_SERVER_KETAMA : MEMCACHED_POINTS_PER_SERVER); - - if (live_servers == 0) - return MEMCACHED_SUCCESS; - - if (live_servers > ptr->continuum_count) - { - memcached_continuum_item_st *new_ptr; - - new_ptr= ptr->call_realloc(ptr, ptr->continuum, - sizeof(memcached_continuum_item_st) * (live_servers + MEMCACHED_CONTINUUM_ADDITION) * points_per_server); - - if (new_ptr == 0) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - ptr->continuum= new_ptr; - ptr->continuum_count= live_servers + MEMCACHED_CONTINUUM_ADDITION; - } - - if (is_ketama_weighted) - { - for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index) - { - if (list[host_index].weight == 0) - { - list[host_index].weight = 1; - } - if (!is_auto_ejecting || list[host_index].next_retry <= now.tv_sec) - total_weight += list[host_index].weight; - } - } - - for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index) - { - if (is_auto_ejecting && list[host_index].next_retry > now.tv_sec) - continue; - - if (is_ketama_weighted) - { - float pct = (float)list[host_index].weight / (float)total_weight; - pointer_per_server= (uint32_t) ((floorf((float) (pct * MEMCACHED_POINTS_PER_SERVER_KETAMA / 4 * (float)live_servers + 0.0000000001))) * 4); - pointer_per_hash= 4; -#ifdef DEBUG - printf("ketama_weighted:%s|%d|%llu|%u\n", - list[host_index].hostname, - list[host_index].port, - (unsigned long long)list[host_index].weight, - pointer_per_server); -#endif - } - - - if (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY) - { - for (pointer_index= 0; - pointer_index < pointer_per_server / pointer_per_hash; - pointer_index++) - { - char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; - size_t sort_host_length; - - // Spymemcached ketema key format is: hostname/ip:port-index - // If hostname is not available then: /ip:port-index - sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, - "/%s:%d-%d", - list[host_index].hostname, - list[host_index].port, - pointer_index); -#ifdef DEBUG - printf("update_continuum: key is %s\n", sort_host); -#endif - - WATCHPOINT_ASSERT(sort_host_length); - - if (is_ketama_weighted) - { - unsigned int i; - for (i = 0; i < pointer_per_hash; i++) - { - value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i); - ptr->continuum[continuum_index].index= host_index; - ptr->continuum[continuum_index++].value= value; - } - } - else - { - value= memcached_generate_hash_value(sort_host, sort_host_length, ptr->hash_continuum); - ptr->continuum[continuum_index].index= host_index; - ptr->continuum[continuum_index++].value= value; - } - } - } - else - { - for (pointer_index= 1; - pointer_index <= pointer_per_server / pointer_per_hash; - pointer_index++) - { - char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= ""; - size_t sort_host_length; - - if (list[host_index].port == MEMCACHED_DEFAULT_PORT) - { - sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, - "%s-%d", - list[host_index].hostname, - pointer_index - 1); - } - else - { - sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH, - "%s:%d-%d", - list[host_index].hostname, - list[host_index].port, pointer_index - 1); - } - - WATCHPOINT_ASSERT(sort_host_length); - - if (is_ketama_weighted) - { - unsigned int i; - for (i = 0; i < pointer_per_hash; i++) - { - value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i); - ptr->continuum[continuum_index].index= host_index; - ptr->continuum[continuum_index++].value= value; - } - } - else - { - value= memcached_generate_hash_value(sort_host, sort_host_length, ptr->hash_continuum); - ptr->continuum[continuum_index].index= host_index; - ptr->continuum[continuum_index++].value= value; - } - } - } - - pointer_counter+= pointer_per_server; - } - - WATCHPOINT_ASSERT(ptr); - WATCHPOINT_ASSERT(ptr->continuum); - WATCHPOINT_ASSERT(ptr->number_of_hosts * MEMCACHED_POINTS_PER_SERVER <= MEMCACHED_CONTINUUM_SIZE); - ptr->continuum_points_counter= pointer_counter; - qsort(ptr->continuum, ptr->continuum_points_counter, sizeof(memcached_continuum_item_st), continuum_item_cmp); - -#ifdef DEBUG - for (pointer_index= 0; ptr->number_of_hosts && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++) - { - WATCHPOINT_ASSERT(ptr->continuum[pointer_index].value <= ptr->continuum[pointer_index + 1].value); - } -#endif - - return MEMCACHED_SUCCESS; -} - - -memcached_return_t memcached_server_push(memcached_st *ptr, memcached_server_st *list) -{ - unsigned int x; - uint16_t count; - memcached_server_st *new_host_list; - - if (!list) - return MEMCACHED_SUCCESS; - - count= list[0].count; - new_host_list= ptr->call_realloc(ptr, ptr->hosts, - sizeof(memcached_server_st) * (count + ptr->number_of_hosts)); - - if (!new_host_list) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - ptr->hosts= new_host_list; - - for (x= 0; x < count; x++) - { - if ((ptr->flags.use_udp && list[x].type != MEMCACHED_CONNECTION_UDP) - || ((list[x].type == MEMCACHED_CONNECTION_UDP) - && ! (ptr->flags.use_udp)) ) - return MEMCACHED_INVALID_HOST_PROTOCOL; - - WATCHPOINT_ASSERT(list[x].hostname[0] != 0); - memcached_server_create(ptr, &ptr->hosts[ptr->number_of_hosts]); - /* TODO check return type */ - (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], list[x].hostname, - list[x].port, list[x].weight, list[x].type); - ptr->number_of_hosts++; - } - ptr->hosts[0].count= (uint16_t) ptr->number_of_hosts; - - return run_distribution(ptr); -} - -memcached_return_t memcached_server_add_unix_socket(memcached_st *ptr, - const char *filename) -{ - return memcached_server_add_unix_socket_with_weight(ptr, filename, 0); -} - -memcached_return_t memcached_server_add_unix_socket_with_weight(memcached_st *ptr, - const char *filename, - uint32_t weight) -{ - if (!filename) - return MEMCACHED_FAILURE; - - return server_add(ptr, filename, 0, weight, MEMCACHED_CONNECTION_UNIX_SOCKET); -} - -memcached_return_t memcached_server_add_udp(memcached_st *ptr, - const char *hostname, - in_port_t port) -{ - return memcached_server_add_udp_with_weight(ptr, hostname, port, 0); -} - -memcached_return_t memcached_server_add_udp_with_weight(memcached_st *ptr, - const char *hostname, - in_port_t port, - uint32_t weight) -{ - if (!port) - port= MEMCACHED_DEFAULT_PORT; - - if (!hostname) - hostname= "localhost"; - - return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_UDP); -} - -memcached_return_t memcached_server_add(memcached_st *ptr, - const char *hostname, - in_port_t port) -{ - return memcached_server_add_with_weight(ptr, hostname, port, 0); -} - -memcached_return_t memcached_server_add_with_weight(memcached_st *ptr, - const char *hostname, - in_port_t port, - uint32_t weight) -{ - if (!port) - port= MEMCACHED_DEFAULT_PORT; - - if (!hostname) - hostname= "localhost"; - - return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_TCP); -} - -static memcached_return_t server_add(memcached_st *ptr, const char *hostname, - in_port_t port, - uint32_t weight, - memcached_connection_t type) -{ - memcached_server_st *new_host_list; - - if ( (ptr->flags.use_udp && type != MEMCACHED_CONNECTION_UDP) - || ( (type == MEMCACHED_CONNECTION_UDP) && (! ptr->flags.use_udp) ) ) - return MEMCACHED_INVALID_HOST_PROTOCOL; - - new_host_list= ptr->call_realloc(ptr, ptr->hosts, - sizeof(memcached_server_st) * (ptr->number_of_hosts+1)); - - if (new_host_list == NULL) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - ptr->hosts= new_host_list; - - /* TODO: Check return type */ - (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], hostname, port, weight, type); - ptr->number_of_hosts++; - ptr->hosts[0].count= (uint16_t) ptr->number_of_hosts; - - return run_distribution(ptr); -} - -memcached_return_t memcached_server_remove(memcached_server_st *st_ptr) -{ - uint32_t x, host_index; - memcached_st *ptr= st_ptr->root; - memcached_server_st *list= ptr->hosts; - - for (x= 0, host_index= 0; x < ptr->number_of_hosts; x++) - { - if (strncmp(list[x].hostname, st_ptr->hostname, MEMCACHED_MAX_HOST_LENGTH) != 0 || list[x].port != st_ptr->port) - { - if (host_index != x) - memcpy(list+host_index, list+x, sizeof(memcached_server_st)); - host_index++; - } - } - ptr->number_of_hosts= host_index; - - if (st_ptr->address_info) - { - freeaddrinfo(st_ptr->address_info); - st_ptr->address_info= NULL; - } - run_distribution(ptr); - - return MEMCACHED_SUCCESS; -} - -memcached_server_st *memcached_server_list_append(memcached_server_st *ptr, - const char *hostname, in_port_t port, - memcached_return_t *error) -{ - return memcached_server_list_append_with_weight(ptr, hostname, port, 0, error); -} - -memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr, - const char *hostname, in_port_t port, - uint32_t weight, - memcached_return_t *error) -{ - unsigned int count; - memcached_server_st *new_host_list; - - if (hostname == NULL || error == NULL) - return NULL; - - if (!port) - port= MEMCACHED_DEFAULT_PORT; - - /* Increment count for hosts */ - count= 1; - if (ptr != NULL) - { - count+= ptr[0].count; - } - - new_host_list= (memcached_server_st *)realloc(ptr, sizeof(memcached_server_st) * count); - if (!new_host_list) - { - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return NULL; - } - - /* TODO: Check return type */ - memcached_server_create_with(NULL, &new_host_list[count-1], hostname, port, weight, MEMCACHED_CONNECTION_TCP); - - /* Backwards compatibility hack */ - new_host_list[0].count= (uint16_t) count; - - *error= MEMCACHED_SUCCESS; - return new_host_list; -} - -unsigned int memcached_server_list_count(memcached_server_st *ptr) -{ - if (ptr == NULL) - return 0; - - return ptr[0].count; -} - -void memcached_server_list_free(memcached_server_st *ptr) -{ - server_list_free(NULL, ptr); -} diff --git a/libmemcached/memcached_internal.h b/libmemcached/memcached_internal.h deleted file mode 100644 index 3e4415dd..00000000 --- a/libmemcached/memcached_internal.h +++ /dev/null @@ -1,35 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Internal functions used by the library. Not for public use! - * - */ - -#ifndef LIBMEMCACHED_MEMCACHED_INTERNAL_H -#define LIBMEMCACHED_MEMCACHED_INTERNAL_H - -#if defined(BUILDING_LIBMEMCACHED) - -#ifdef __cplusplus -extern "C" { -#endif - -LIBMEMCACHED_LOCAL -void libmemcached_free(memcached_st *ptr, void *mem); -LIBMEMCACHED_LOCAL -void *libmemcached_malloc(memcached_st *ptr, const size_t size); -LIBMEMCACHED_LOCAL -void *libmemcached_realloc(memcached_st *ptr, void *mem, const size_t size); -LIBMEMCACHED_LOCAL -void *libmemcached_calloc(memcached_st *ptr, size_t nelem, size_t size); - -#ifdef __cplusplus -} -#endif - -#endif /* BUILDING_LIBMEMCACHED */ -#endif /* LIBMEMCACHED_MEMCACHED_INTERNAL_H */ diff --git a/libmemcached/memcached_io.c b/libmemcached/memcached_io.c deleted file mode 100644 index fd86437f..00000000 --- a/libmemcached/memcached_io.c +++ /dev/null @@ -1,644 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Server IO, Not public! - * - */ - - -#include "common.h" -#include "memcached_io.h" -#include -#include - -typedef enum { - MEM_READ, - MEM_WRITE -} memc_read_or_write; - -static ssize_t io_flush(memcached_server_st *ptr, memcached_return_t *error); -static void increment_udp_message_id(memcached_server_st *ptr); - -static memcached_return_t io_wait(memcached_server_st *ptr, - memc_read_or_write read_or_write) -{ - struct pollfd fds= { - .fd= ptr->fd, - .events = POLLIN - }; - int error; - - unlikely (read_or_write == MEM_WRITE) /* write */ - fds.events= POLLOUT; - - /* - ** We are going to block on write, but at least on Solaris we might block - ** on write if we haven't read anything from our input buffer.. - ** Try to purge the input buffer if we don't do any flow control in the - ** application layer (just sending a lot of data etc) - ** The test is moved down in the purge function to avoid duplication of - ** the test. - */ - if (read_or_write == MEM_WRITE) - { - memcached_return_t rc= memcached_purge(ptr); - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) - return MEMCACHED_FAILURE; - } - - int timeout= ptr->root->poll_timeout; - if (ptr->root->flags.no_block == false) - timeout= -1; - - error= poll(&fds, 1, timeout); - - if (error == 1) - return MEMCACHED_SUCCESS; - else if (error == 0) - return MEMCACHED_TIMEOUT; - - /* Imposssible for anything other then -1 */ - WATCHPOINT_ASSERT(error == -1); - memcached_quit_server(ptr, 1); - - return MEMCACHED_FAILURE; -} - -/** - * Try to fill the input buffer for a server with as much - * data as possible. - * - * @param ptr the server to pack - */ -static bool repack_input_buffer(memcached_server_st *ptr) -{ - if (ptr->read_ptr != ptr->read_buffer) - { - /* Move all of the data to the beginning of the buffer so - ** that we can fit more data into the buffer... - */ - memmove(ptr->read_buffer, ptr->read_ptr, ptr->read_buffer_length); - ptr->read_ptr= ptr->read_buffer; - ptr->read_data_length= ptr->read_buffer_length; - } - - /* There is room in the buffer, try to fill it! */ - if (ptr->read_buffer_length != MEMCACHED_MAX_BUFFER) - { - /* Just try a single read to grab what's available */ - ssize_t nr= read(ptr->fd, - ptr->read_ptr + ptr->read_data_length, - MEMCACHED_MAX_BUFFER - ptr->read_data_length); - - if (nr > 0) - { - ptr->read_data_length+= (size_t)nr; - ptr->read_buffer_length+= (size_t)nr; - return true; - } - } - return false; -} - -/** - * If the we have callbacks connected to this server structure - * we may start process the input queue and fire the callbacks - * for the incomming messages. This function is _only_ called - * when the input buffer is full, so that we _know_ that we have - * at least _one_ message to process. - * - * @param ptr the server to star processing iput messages for - * @return true if we processed anything, false otherwise - */ -static bool process_input_buffer(memcached_server_st *ptr) -{ - /* - ** We might be able to process some of the response messages if we - ** have a callback set up - */ - if (ptr->root->callbacks != NULL && ptr->root->flags.use_udp == false) - { - /* - * We might have responses... try to read them out and fire - * callbacks - */ - memcached_callback_st cb= *ptr->root->callbacks; - - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - memcached_return_t error; - error= memcached_response(ptr, buffer, sizeof(buffer), - &ptr->root->result); - if (error == MEMCACHED_SUCCESS) - { - for (unsigned int x= 0; x < cb.number_of_callback; x++) - { - error= (*cb.callback[x])(ptr->root, &ptr->root->result, cb.context); - if (error != MEMCACHED_SUCCESS) - break; - } - - /* @todo what should I do with the error message??? */ - } - /* @todo what should I do with other error messages?? */ - return true; - } - - return false; -} - -#ifdef UNUSED -void memcached_io_preread(memcached_st *ptr) -{ - unsigned int x; - - return; - - for (x= 0; x < ptr->number_of_hosts; x++) - { - if (memcached_server_response_count(ptr, x) && - ptr->hosts[x].read_data_length < MEMCACHED_MAX_BUFFER ) - { - size_t data_read; - - data_read= read(ptr->hosts[x].fd, - ptr->hosts[x].read_ptr + ptr->hosts[x].read_data_length, - MEMCACHED_MAX_BUFFER - ptr->hosts[x].read_data_length); - if (data_read == -1) - continue; - - ptr->hosts[x].read_buffer_length+= data_read; - ptr->hosts[x].read_data_length+= data_read; - } - } -} -#endif - -memcached_return_t memcached_io_read(memcached_server_st *ptr, - void *buffer, size_t length, ssize_t *nread) -{ - char *buffer_ptr; - - buffer_ptr= buffer; - - while (length) - { - if (!ptr->read_buffer_length) - { - ssize_t data_read; - - while (1) - { - data_read= read(ptr->fd, ptr->read_buffer, MEMCACHED_MAX_BUFFER); - if (data_read > 0) - break; - else if (data_read == -1) - { - ptr->cached_errno= errno; - memcached_return_t rc= MEMCACHED_UNKNOWN_READ_FAILURE; - switch (errno) - { - case EAGAIN: - case EINTR: - if ((rc= io_wait(ptr, MEM_READ)) == MEMCACHED_SUCCESS) - continue; - /* fall through */ - - default: - { - memcached_quit_server(ptr, 1); - *nread= -1; - return rc; - } - } - } - else - { - /* - EOF. Any data received so far is incomplete - so discard it. This always reads by byte in case of TCP - and protocol enforcement happens at memcached_response() - looking for '\n'. We do not care for UDB which requests 8 bytes - at once. Generally, this means that connection went away. Since - for blocking I/O we do not return 0 and for non-blocking case - it will return EGAIN if data is not immediatly available. - */ - memcached_quit_server(ptr, 1); - *nread= -1; - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - - ptr->io_bytes_sent = 0; - ptr->read_data_length= (size_t) data_read; - ptr->read_buffer_length= (size_t) data_read; - ptr->read_ptr= ptr->read_buffer; - } - - if (length > 1) - { - size_t difference; - - difference= (length > ptr->read_buffer_length) ? ptr->read_buffer_length : length; - - memcpy(buffer_ptr, ptr->read_ptr, difference); - length -= difference; - ptr->read_ptr+= difference; - ptr->read_buffer_length-= difference; - buffer_ptr+= difference; - } - else - { - *buffer_ptr= *ptr->read_ptr; - ptr->read_ptr++; - ptr->read_buffer_length--; - buffer_ptr++; - break; - } - } - - ptr->server_failure_counter= 0; - *nread = (ssize_t)(buffer_ptr - (char*)buffer); - return MEMCACHED_SUCCESS; -} - -ssize_t memcached_io_write(memcached_server_st *ptr, - const void *buffer, size_t length, char with_flush) -{ - size_t original_length; - const char* buffer_ptr; - - WATCHPOINT_ASSERT(ptr->fd != -1); - - original_length= length; - buffer_ptr= buffer; - - while (length) - { - char *write_ptr; - size_t should_write; - size_t buffer_end; - - if (ptr->type == MEMCACHED_CONNECTION_UDP) - { - //UDP does not support partial writes - buffer_end= MAX_UDP_DATAGRAM_LENGTH; - should_write= length; - if (ptr->write_buffer_offset + should_write > buffer_end) - return -1; - } - else - { - buffer_end= MEMCACHED_MAX_BUFFER; - should_write= buffer_end - ptr->write_buffer_offset; - should_write= (should_write < length) ? should_write : length; - } - - write_ptr= ptr->write_buffer + ptr->write_buffer_offset; - memcpy(write_ptr, buffer_ptr, should_write); - ptr->write_buffer_offset+= should_write; - buffer_ptr+= should_write; - length-= should_write; - - if (ptr->write_buffer_offset == buffer_end && ptr->type != MEMCACHED_CONNECTION_UDP) - { - memcached_return_t rc; - ssize_t sent_length; - - WATCHPOINT_ASSERT(ptr->fd != -1); - sent_length= io_flush(ptr, &rc); - if (sent_length == -1) - return -1; - - /* If io_flush calls memcached_purge, sent_length may be 0 */ - unlikely (sent_length != 0) - { - WATCHPOINT_ASSERT(sent_length == (ssize_t)buffer_end); - } - } - } - - if (with_flush) - { - memcached_return_t rc; - WATCHPOINT_ASSERT(ptr->fd != -1); - if (io_flush(ptr, &rc) == -1) - return -1; - } - - return (ssize_t) original_length; -} - -memcached_return_t memcached_io_close(memcached_server_st *ptr) -{ - int r; - - if (ptr->fd == -1) - return MEMCACHED_SUCCESS; - - /* in case of death shutdown to avoid blocking at close() */ - if (1) - { - r= shutdown(ptr->fd, SHUT_RDWR); - -#ifdef DEBUG - if (r && errno != ENOTCONN) - { - WATCHPOINT_NUMBER(ptr->fd); - WATCHPOINT_ERRNO(errno); - WATCHPOINT_ASSERT(errno); - } -#endif - } - - r= close(ptr->fd); -#ifdef DEBUG - if (r != 0) - WATCHPOINT_ERRNO(errno); -#endif - - return MEMCACHED_SUCCESS; -} - -memcached_server_st *memcached_io_get_readable_server(memcached_st *memc) -{ -#define MAX_SERVERS_TO_POLL 100 - struct pollfd fds[MAX_SERVERS_TO_POLL]; - unsigned int host_index= 0; - - for (unsigned int x= 0; - x< memc->number_of_hosts && host_index < MAX_SERVERS_TO_POLL; - ++x) - { - if (memc->hosts[x].read_buffer_length > 0) /* I have data in the buffer */ - return &memc->hosts[x]; - - if (memcached_server_response_count(&memc->hosts[x]) > 0) - { - fds[host_index].events = POLLIN; - fds[host_index].revents = 0; - fds[host_index].fd = memc->hosts[x].fd; - ++host_index; - } - } - - if (host_index < 2) - { - /* We have 0 or 1 server with pending events.. */ - for (unsigned int x= 0; x< memc->number_of_hosts; ++x) - if (memcached_server_response_count(&memc->hosts[x]) > 0) - return &memc->hosts[x]; - - return NULL; - } - - int err= poll(fds, host_index, memc->poll_timeout); - switch (err) { - case -1: - memc->cached_errno = errno; - /* FALLTHROUGH */ - case 0: - break; - default: - for (unsigned int x= 0; x < host_index; ++x) - if (fds[x].revents & POLLIN) - for (unsigned int y= 0; y < memc->number_of_hosts; ++y) - if (memc->hosts[y].fd == fds[x].fd) - return &memc->hosts[y]; - } - - return NULL; -} - -static ssize_t io_flush(memcached_server_st *ptr, - memcached_return_t *error) -{ - /* - ** We might want to purge the input buffer if we haven't consumed - ** any output yet... The test for the limits is the purge is inline - ** in the purge function to avoid duplicating the logic.. - */ - { - memcached_return_t rc; - WATCHPOINT_ASSERT(ptr->fd != -1); - rc= memcached_purge(ptr); - - if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED) - return -1; - } - ssize_t sent_length; - size_t return_length; - char *local_write_ptr= ptr->write_buffer; - size_t write_length= ptr->write_buffer_offset; - - *error= MEMCACHED_SUCCESS; - - WATCHPOINT_ASSERT(ptr->fd != -1); - - // UDP Sanity check, make sure that we are not sending somthing too big - if (ptr->type == MEMCACHED_CONNECTION_UDP && write_length > MAX_UDP_DATAGRAM_LENGTH) - return -1; - - if (ptr->write_buffer_offset == 0 || (ptr->type == MEMCACHED_CONNECTION_UDP - && ptr->write_buffer_offset == UDP_DATAGRAM_HEADER_LENGTH)) - return 0; - - /* Looking for memory overflows */ -#if defined(DEBUG) - if (write_length == MEMCACHED_MAX_BUFFER) - WATCHPOINT_ASSERT(ptr->write_buffer == local_write_ptr); - WATCHPOINT_ASSERT((ptr->write_buffer + MEMCACHED_MAX_BUFFER) >= (local_write_ptr + write_length)); -#endif - - return_length= 0; - while (write_length) - { - WATCHPOINT_ASSERT(ptr->fd != -1); - WATCHPOINT_ASSERT(write_length > 0); - sent_length= 0; - if (ptr->type == MEMCACHED_CONNECTION_UDP) - increment_udp_message_id(ptr); - sent_length= write(ptr->fd, local_write_ptr, write_length); - - if (sent_length == -1) - { - ptr->cached_errno= errno; - switch (errno) - { - case ENOBUFS: - continue; - case EAGAIN: - { - /* - * We may be blocked on write because the input buffer - * is full. Let's check if we have room in our input - * buffer for more data and retry the write before - * waiting.. - */ - if (repack_input_buffer(ptr) || - process_input_buffer(ptr)) - continue; - - memcached_return_t rc; - rc= io_wait(ptr, MEM_WRITE); - - if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_TIMEOUT) - continue; - - memcached_quit_server(ptr, 1); - return -1; - } - default: - memcached_quit_server(ptr, 1); - *error= MEMCACHED_ERRNO; - return -1; - } - } - - if (ptr->type == MEMCACHED_CONNECTION_UDP && - (size_t)sent_length != write_length) - { - memcached_quit_server(ptr, 1); - return -1; - } - - ptr->io_bytes_sent += (uint32_t) sent_length; - - local_write_ptr+= sent_length; - write_length-= (uint32_t) sent_length; - return_length+= (uint32_t) sent_length; - } - - WATCHPOINT_ASSERT(write_length == 0); - // Need to study this assert() WATCHPOINT_ASSERT(return_length == - // ptr->write_buffer_offset); - - // if we are a udp server, the begining of the buffer is reserverd for - // the upd frame header - if (ptr->type == MEMCACHED_CONNECTION_UDP) - ptr->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; - else - ptr->write_buffer_offset= 0; - - return (ssize_t) return_length; -} - -/* - Eventually we will just kill off the server with the problem. -*/ -void memcached_io_reset(memcached_server_st *ptr) -{ - memcached_quit_server(ptr, 1); -} - -/** - * Read a given number of bytes from the server and place it into a specific - * buffer. Reset the IO channel on this server if an error occurs. - */ -memcached_return_t memcached_safe_read(memcached_server_st *ptr, - void *dta, - size_t size) -{ - size_t offset= 0; - char *data= dta; - - while (offset < size) - { - ssize_t nread; - memcached_return_t rc= memcached_io_read(ptr, data + offset, size - offset, - &nread); - if (rc != MEMCACHED_SUCCESS) - return rc; - - offset+= (size_t) nread; - } - - return MEMCACHED_SUCCESS; -} - -memcached_return_t memcached_io_readline(memcached_server_st *ptr, - char *buffer_ptr, - size_t size) -{ - bool line_complete= false; - size_t total_nr= 0; - - while (!line_complete) - { - if (ptr->read_buffer_length == 0) - { - /* - * We don't have any data in the buffer, so let's fill the read - * buffer. Call the standard read function to avoid duplicating - * the logic. - */ - ssize_t nread; - memcached_return_t rc= memcached_io_read(ptr, buffer_ptr, 1, &nread); - if (rc != MEMCACHED_SUCCESS) - return rc; - - if (*buffer_ptr == '\n') - line_complete= true; - - ++buffer_ptr; - ++total_nr; - } - - /* Now let's look in the buffer and copy as we go! */ - while (ptr->read_buffer_length && total_nr < size && !line_complete) - { - *buffer_ptr = *ptr->read_ptr; - if (*buffer_ptr == '\n') - line_complete = true; - --ptr->read_buffer_length; - ++ptr->read_ptr; - ++total_nr; - ++buffer_ptr; - } - - if (total_nr == size) - return MEMCACHED_PROTOCOL_ERROR; - } - - return MEMCACHED_SUCCESS; -} - -/* - * The udp request id consists of two seperate sections - * 1) The thread id - * 2) The message number - * The thread id should only be set when the memcached_st struct is created - * and should not be changed. - * - * The message num is incremented for each new message we send, this function - * extracts the message number from message_id, increments it and then - * writes the new value back into the header - */ -static void increment_udp_message_id(memcached_server_st *ptr) -{ - struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; - uint16_t cur_req= get_udp_datagram_request_id(header); - int msg_num= get_msg_num_from_request_id(cur_req); - int thread_id= get_thread_id_from_request_id(cur_req); - - if (((++msg_num) & UDP_REQUEST_ID_THREAD_MASK) != 0) - msg_num= 0; - - header->request_id= htons((uint16_t) (thread_id | msg_num)); -} - -memcached_return_t memcached_io_init_udp_header(memcached_server_st *ptr, uint16_t thread_id) -{ - if (thread_id > UDP_REQUEST_ID_MAX_THREAD_ID) - return MEMCACHED_FAILURE; - - struct udp_datagram_header_st *header= (struct udp_datagram_header_st *)ptr->write_buffer; - header->request_id= htons((uint16_t) (generate_udp_request_thread_id(thread_id))); - header->num_datagrams= htons(1); - header->sequence_number= htons(0); - - return MEMCACHED_SUCCESS; -} diff --git a/libmemcached/memcached_io.h b/libmemcached/memcached_io.h deleted file mode 100644 index 03d63b55..00000000 --- a/libmemcached/memcached_io.h +++ /dev/null @@ -1,71 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Server IO, Not public! - * - */ - -#ifndef LIBMEMCACHED_MEMCACHED_IO_H -#define LIBMEMCACHED_MEMCACHED_IO_H - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(BUILDING_LIBMEMCACHED) - -#include "libmemcached/memcached.h" - -#define MAX_UDP_DATAGRAM_LENGTH 1400 -#define UDP_DATAGRAM_HEADER_LENGTH 8 -#define UDP_REQUEST_ID_MSG_SIG_DIGITS 10 -#define UDP_REQUEST_ID_THREAD_MASK 0xFFFF << UDP_REQUEST_ID_MSG_SIG_DIGITS -#define get_udp_datagram_request_id(A) ntohs((A)->request_id) -#define get_udp_datagram_seq_num(A) ntohs((A)->sequence_number) -#define get_udp_datagram_num_datagrams(A) ntohs((A)->num_datagrams) -#define get_msg_num_from_request_id(A) ( (A) & (~(UDP_REQUEST_ID_THREAD_MASK)) ) -#define get_thread_id_from_request_id(A) ( (A) & (UDP_REQUEST_ID_THREAD_MASK) ) >> UDP_REQUEST_ID_MSG_SIG_DIGITS -#define generate_udp_request_thread_id(A) (A) << UDP_REQUEST_ID_MSG_SIG_DIGITS -#define UDP_REQUEST_ID_MAX_THREAD_ID get_thread_id_from_request_id(0xFFFF) - -struct udp_datagram_header_st { - uint16_t request_id; - uint16_t sequence_number; - uint16_t num_datagrams; - uint16_t reserved; -}; - -ssize_t memcached_io_write(memcached_server_st *ptr, - const void *buffer, size_t length, char with_flush); -void memcached_io_reset(memcached_server_st *ptr); -memcached_return_t memcached_io_read(memcached_server_st *ptr, - void *buffer, size_t length, ssize_t *nread); -/* Read a line (terminated by '\n') into the buffer */ -memcached_return_t memcached_io_readline(memcached_server_st *ptr, - char *buffer_ptr, - size_t size); -memcached_return_t memcached_io_close(memcached_server_st *ptr); -/* Read n bytes of data from the server and store them in dta */ -memcached_return_t memcached_safe_read(memcached_server_st *ptr, - void *dta, - size_t size); -/* Read a single response from the server */ -memcached_return_t memcached_read_one_response(memcached_server_st *ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result); -memcached_return_t memcached_io_init_udp_header(memcached_server_st *ptr, - uint16_t thread_id); - -memcached_server_st *memcached_io_get_readable_server(memcached_st *memc); - -#endif /* BUILDING_LIBMEMCACHED */ - -#ifdef __cplusplus -} -#endif - -#endif /* LIBMEMCACHED_MEMCACHED_IO_H */ diff --git a/libmemcached/memcached_key.c b/libmemcached/memcached_key.c deleted file mode 100644 index 3b16b273..00000000 --- a/libmemcached/memcached_key.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "common.h" - -memcached_return_t memcached_key_test(const char * const *keys, - const size_t *key_length, - size_t number_of_keys) -{ - uint32_t x; - memcached_return_t rc; - - for (x= 0; x < number_of_keys; x++) - { - size_t y; - - rc= memcached_validate_key_length(*(key_length + x), false); - if (rc != MEMCACHED_SUCCESS) - return rc; - - for (y= 0; y < *(key_length + x); y++) - { - if ((isgraph(keys[x][y])) == 0) - return MEMCACHED_BAD_KEY_PROVIDED; - } - } - - return MEMCACHED_SUCCESS; -} - diff --git a/libmemcached/memcached_parse.c b/libmemcached/memcached_parse.c deleted file mode 100644 index 73cc9dfe..00000000 --- a/libmemcached/memcached_parse.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - I debated about putting this in the client library since it does an - action I don't really believe belongs in the library. - - Frankly its too damn useful not to be here though. -*/ - -#include "common.h" - -memcached_server_st *memcached_servers_parse(const char *server_strings) -{ - char *string; - uint32_t port; - uint32_t weight; - const char *begin_ptr; - const char *end_ptr; - memcached_server_st *servers= NULL; - memcached_return_t rc; - - WATCHPOINT_ASSERT(server_strings); - - end_ptr= server_strings + strlen(server_strings); - - for (begin_ptr= server_strings, string= index(server_strings, ','); - begin_ptr != end_ptr; - string= index(begin_ptr, ',')) - { - char buffer[HUGE_STRING_LEN]; - char *ptr, *ptr2; - port= 0; - weight= 0; - - if (string) - { - memcpy(buffer, begin_ptr, (size_t) (string - begin_ptr)); - buffer[(unsigned int)(string - begin_ptr)]= 0; - begin_ptr= string+1; - } - else - { - size_t length= strlen(begin_ptr); - memcpy(buffer, begin_ptr, length); - buffer[length]= 0; - begin_ptr= end_ptr; - } - - ptr= index(buffer, ':'); - - if (ptr) - { - ptr[0]= 0; - - ptr++; - - port= (uint32_t) strtoul(ptr, (char **)NULL, 10); - - ptr2= index(ptr, ' '); - if (! ptr2) - ptr2= index(ptr, ':'); - if (ptr2) - { - ptr2++; - weight = (uint32_t) strtoul(ptr2, (char **)NULL, 10); - } - } - - servers= memcached_server_list_append_with_weight(servers, buffer, port, weight, &rc); - - if (isspace(*begin_ptr)) - begin_ptr++; - } - - return servers; -} diff --git a/libmemcached/memcached_pool.h b/libmemcached/memcached_pool.h deleted file mode 100644 index 84dab3dd..00000000 --- a/libmemcached/memcached_pool.h +++ /dev/null @@ -1,51 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Connection pool implementation for libmemcached. - * - */ - - -#ifndef MEMCACHED_POOL_H -#define MEMCACHED_POOL_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct memcached_pool_st; -typedef struct memcached_pool_st memcached_pool_st; - -LIBMEMCACHED_API -memcached_pool_st *memcached_pool_create(memcached_st* mmc, uint32_t initial, - uint32_t max); -LIBMEMCACHED_API -memcached_st* memcached_pool_destroy(memcached_pool_st* pool); -LIBMEMCACHED_API -memcached_st* memcached_pool_pop(memcached_pool_st* pool, - bool block, - memcached_return_t* rc); -LIBMEMCACHED_API -memcached_return_t memcached_pool_push(memcached_pool_st* pool, - memcached_st* mmc); - -LIBMEMCACHED_API -memcached_return_t memcached_pool_behavior_set(memcached_pool_st *ptr, - memcached_behavior_t flag, - uint64_t data); -LIBMEMCACHED_API -memcached_return_t memcached_pool_behavior_get(memcached_pool_st *ptr, - memcached_behavior_t flag, - uint64_t *value); - -#ifdef __cplusplus -} -#endif - -#endif /* MEMCACHED_POOL_H */ diff --git a/libmemcached/memcached_purge.c b/libmemcached/memcached_purge.c deleted file mode 100644 index 9042142b..00000000 --- a/libmemcached/memcached_purge.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "common.h" -#include "memcached_io.h" -#include "memcached_constants.h" - -memcached_return_t memcached_purge(memcached_server_st *ptr) -{ - uint32_t x; - memcached_return_t ret= MEMCACHED_SUCCESS; - - if (ptr->root->options.is_purging || /* already purging */ - (memcached_server_response_count(ptr) < ptr->root->io_msg_watermark && - ptr->io_bytes_sent < ptr->root->io_bytes_watermark) || - (ptr->io_bytes_sent >= ptr->root->io_bytes_watermark && - memcached_server_response_count(ptr) < 2)) - { - return MEMCACHED_SUCCESS; - } - - /* memcached_io_write and memcached_response may call memcached_purge - so we need to be able stop any recursion.. */ - ptr->root->options.is_purging= true; - - WATCHPOINT_ASSERT(ptr->fd != -1); - /* Force a flush of the buffer to ensure that we don't have the n-1 pending - requests buffered up.. */ - if (memcached_io_write(ptr, NULL, 0, 1) == -1) - { - ptr->root->options.is_purging= true; - return MEMCACHED_WRITE_FAILURE; - } - WATCHPOINT_ASSERT(ptr->fd != -1); - - uint32_t no_msg= memcached_server_response_count(ptr) - 1; - if (no_msg > 0) - { - memcached_result_st result; - memcached_result_st *result_ptr; - char buffer[SMALL_STRING_LEN]; - - /* - * We need to increase the timeout, because we might be waiting for - * data to be sent from the server (the commands was in the output buffer - * and just flushed - */ - int32_t timeo= ptr->root->poll_timeout; - ptr->root->poll_timeout= 2000; - - result_ptr= memcached_result_create(ptr->root, &result); - WATCHPOINT_ASSERT(result_ptr); - - for (x= 0; x < no_msg; x++) - { - memcached_result_reset(result_ptr); - memcached_return_t rc= memcached_read_one_response(ptr, buffer, - sizeof (buffer), - result_ptr); - /* - * Purge doesn't care for what kind of command results that is received. - * The only kind of errors I care about if is I'm out of sync with the - * protocol or have problems reading data from the network.. - */ - if (rc== MEMCACHED_PROTOCOL_ERROR || rc == MEMCACHED_UNKNOWN_READ_FAILURE) - { - WATCHPOINT_ERROR(rc); - ret = rc; - memcached_io_reset(ptr); - } - } - - memcached_result_free(result_ptr); - ptr->root->poll_timeout= timeo; - } - ptr->root->options.is_purging= false; - - return ret; -} diff --git a/libmemcached/memcached_quit.c b/libmemcached/memcached_quit.c deleted file mode 100644 index 3c117ade..00000000 --- a/libmemcached/memcached_quit.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "common.h" - -/* - This closes all connections (forces flush of input as well). - - Maybe add a host specific, or key specific version? - - The reason we send "quit" is that in case we have buffered IO, this - will force data to be completed. -*/ - -void memcached_quit_server(memcached_server_st *ptr, uint8_t io_death) -{ - if (ptr->fd != -1) - { - if (io_death == 0 && ptr->type != MEMCACHED_CONNECTION_UDP) - { - memcached_return_t rc; - char buffer[MEMCACHED_MAX_BUFFER]; - - if (ptr->root->flags.binary_protocol) - { - protocol_binary_request_quit request = {.bytes= {0}}; - request.message.header.request.magic = PROTOCOL_BINARY_REQ; - request.message.header.request.opcode = PROTOCOL_BINARY_CMD_QUIT; - request.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; - rc= memcached_do(ptr, request.bytes, sizeof(request.bytes), 1); - } - else - rc= memcached_do(ptr, "quit\r\n", 6, 1); - - WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_FETCH_NOTFINISHED); - - /* read until socket is closed, or there is an error - * closing the socket before all data is read - * results in server throwing away all data which is - * not read - */ - ssize_t nread; - while (memcached_io_read(ptr, buffer, sizeof(buffer)/sizeof(*buffer), - &nread) == MEMCACHED_SUCCESS); - - /* - * memcached_io_read may call memcached_quit_server with io_death if - * it encounters problems, but we don't care about those occurences. - * The intention of that loop is to drain the data sent from the - * server to ensure that the server processed all of the data we - * sent to the server. - */ - ptr->server_failure_counter= 0; - } - memcached_io_close(ptr); - - ptr->fd= -1; - ptr->write_buffer_offset= (size_t) ((ptr->type == MEMCACHED_CONNECTION_UDP) ? UDP_DATAGRAM_HEADER_LENGTH : 0); - ptr->read_buffer_length= 0; - ptr->read_ptr= ptr->read_buffer; - memcached_server_response_reset(ptr); - } - - if(io_death) ptr->server_failure_counter++; -} - -void memcached_quit(memcached_st *ptr) -{ - unsigned int x; - - if (ptr->hosts == NULL || - ptr->number_of_hosts == 0) - return; - - if (ptr->hosts && ptr->number_of_hosts) - { - for (x= 0; x < ptr->number_of_hosts; x++) - memcached_quit_server(&ptr->hosts[x], 0); - } -} diff --git a/libmemcached/memcached_response.c b/libmemcached/memcached_response.c deleted file mode 100644 index cb297428..00000000 --- a/libmemcached/memcached_response.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - Memcached library - - memcached_response() is used to determine the return result - from an issued command. -*/ - -#include "common.h" -#include "memcached_io.h" - -static memcached_return_t textual_read_one_response(memcached_server_st *ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result); -static memcached_return_t binary_read_one_response(memcached_server_st *ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result); - -memcached_return_t memcached_read_one_response(memcached_server_st *ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - memcached_server_response_decrement(ptr); - - if (result == NULL) - result = &ptr->root->result; - - memcached_return_t rc; - if (ptr->root->flags.binary_protocol) - rc= binary_read_one_response(ptr, buffer, buffer_length, result); - else - rc= textual_read_one_response(ptr, buffer, buffer_length, result); - - unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE || - rc == MEMCACHED_PROTOCOL_ERROR || - rc == MEMCACHED_CLIENT_ERROR || - rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) - memcached_io_reset(ptr); - - return rc; -} - -memcached_return_t memcached_response(memcached_server_st *ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - /* We may have old commands in the buffer not set, first purge */ - if (ptr->root->flags.no_block) - (void)memcached_io_write(ptr, NULL, 0, 1); - - /* - * The previous implementation purged all pending requests and just - * returned the last one. Purge all pending messages to ensure backwards - * compatibility. - */ - if (ptr->root->flags.binary_protocol == false) - while (memcached_server_response_count(ptr) > 1) - { - memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result); - - unlikely (rc != MEMCACHED_END && - rc != MEMCACHED_STORED && - rc != MEMCACHED_SUCCESS && - rc != MEMCACHED_STAT && - rc != MEMCACHED_DELETED && - rc != MEMCACHED_NOTFOUND && - rc != MEMCACHED_NOTSTORED && - rc != MEMCACHED_DATA_EXISTS) - return rc; - } - - return memcached_read_one_response(ptr, buffer, buffer_length, result); -} - -static memcached_return_t textual_value_fetch(memcached_server_st *ptr, - char *buffer, - memcached_result_st *result) -{ - memcached_return_t rc= MEMCACHED_SUCCESS; - char *string_ptr; - char *end_ptr; - char *next_ptr; - size_t value_length; - size_t to_read; - char *value_ptr; - - if (ptr->root->flags.use_udp) - return MEMCACHED_NOT_SUPPORTED; - - WATCHPOINT_ASSERT(ptr->root); - end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE; - - memcached_result_reset(result); - - string_ptr= buffer; - string_ptr+= 6; /* "VALUE " */ - - - /* We load the key */ - { - char *key; - size_t prefix_length; - - key= result->key; - result->key_length= 0; - - for (prefix_length= ptr->root->prefix_key_length; !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++) - { - if (prefix_length == 0) - { - *key= *string_ptr; - key++; - result->key_length++; - } - else - prefix_length--; - } - result->key[result->key_length]= 0; - } - - if (end_ptr == string_ptr) - goto read_error; - - /* Flags fetch move past space */ - string_ptr++; - if (end_ptr == string_ptr) - goto read_error; - for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); - result->flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10); - - if (end_ptr == string_ptr) - goto read_error; - - /* Length fetch move past space*/ - string_ptr++; - if (end_ptr == string_ptr) - goto read_error; - - for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); - value_length= (size_t)strtoull(next_ptr, &string_ptr, 10); - - if (end_ptr == string_ptr) - goto read_error; - - /* Skip spaces */ - if (*string_ptr == '\r') - { - /* Skip past the \r\n */ - string_ptr+= 2; - } - else - { - string_ptr++; - for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); - result->cas= strtoull(next_ptr, &string_ptr, 10); - } - - if (end_ptr < string_ptr) - goto read_error; - - /* We add two bytes so that we can walk the \r\n */ - rc= memcached_string_check(&result->value, value_length+2); - if (rc != MEMCACHED_SUCCESS) - { - value_length= 0; - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - } - - value_ptr= memcached_string_value(&result->value); - /* - We read the \r\n into the string since not doing so is more - cycles then the waster of memory to do so. - - We are null terminating through, which will most likely make - some people lazy about using the return length. - */ - to_read= (value_length) + 2; - ssize_t read_length= 0; - memcached_return_t rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length); - if (rrc != MEMCACHED_SUCCESS) - return rrc; - - if (read_length != (ssize_t)(value_length + 2)) - { - goto read_error; - } - -/* This next bit blows the API, but this is internal....*/ - { - char *char_ptr; - char_ptr= memcached_string_value(&result->value);; - char_ptr[value_length]= 0; - char_ptr[value_length + 1]= 0; - memcached_string_set_length(&result->value, value_length); - } - - return MEMCACHED_SUCCESS; - -read_error: - memcached_io_reset(ptr); - - return MEMCACHED_PARTIAL_READ; -} - -static memcached_return_t textual_read_one_response(memcached_server_st *ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length); - if (rc != MEMCACHED_SUCCESS) - return rc; - - switch(buffer[0]) - { - case 'V': /* VALUE || VERSION */ - if (buffer[1] == 'A') /* VALUE */ - { - /* We add back in one because we will need to search for END */ - memcached_server_response_increment(ptr); - return textual_value_fetch(ptr, buffer, result); - } - else if (buffer[1] == 'E') /* VERSION */ - { - return MEMCACHED_SUCCESS; - } - else - { - WATCHPOINT_STRING(buffer); - WATCHPOINT_ASSERT(0); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - case 'O': /* OK */ - return MEMCACHED_SUCCESS; - case 'S': /* STORED STATS SERVER_ERROR */ - { - if (buffer[2] == 'A') /* STORED STATS */ - { - memcached_server_response_increment(ptr); - return MEMCACHED_STAT; - } - else if (buffer[1] == 'E') /* SERVER_ERROR */ - { - char *rel_ptr; - char *startptr= buffer + 13, *endptr= startptr; - - while (*endptr != '\r' && *endptr != '\n') endptr++; - - /* - Yes, we could make this "efficent" but to do that we would need - to maintain more state for the size of the buffer. Why waste - memory in the struct, which is important, for something that - rarely should happen? - */ - rel_ptr= (char *)ptr->root->call_realloc(ptr->root, - ptr->cached_server_error, - (size_t) (endptr - startptr + 1)); - - if (rel_ptr == NULL) - { - /* If we happened to have some memory, we just null it since we don't know the size */ - if (ptr->cached_server_error) - ptr->cached_server_error[0]= 0; - return MEMCACHED_SERVER_ERROR; - } - ptr->cached_server_error= rel_ptr; - - memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr)); - ptr->cached_server_error[endptr - startptr]= 0; - return MEMCACHED_SERVER_ERROR; - } - else if (buffer[1] == 'T') - return MEMCACHED_STORED; - else - { - WATCHPOINT_STRING(buffer); - WATCHPOINT_ASSERT(0); - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - case 'D': /* DELETED */ - return MEMCACHED_DELETED; - case 'N': /* NOT_FOUND */ - { - if (buffer[4] == 'F') - return MEMCACHED_NOTFOUND; - else if (buffer[4] == 'S') - return MEMCACHED_NOTSTORED; - else - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - case 'E': /* PROTOCOL ERROR or END */ - { - if (buffer[1] == 'N') - return MEMCACHED_END; - else if (buffer[1] == 'R') - return MEMCACHED_PROTOCOL_ERROR; - else if (buffer[1] == 'X') - return MEMCACHED_DATA_EXISTS; - else - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - case 'I': /* CLIENT ERROR */ - /* We add back in one because we will need to search for END */ - memcached_server_response_increment(ptr); - return MEMCACHED_ITEM; - case 'C': /* CLIENT ERROR */ - return MEMCACHED_CLIENT_ERROR; - default: - { - unsigned long long auto_return_value; - - if (sscanf(buffer, "%llu", &auto_return_value) == 1) - return MEMCACHED_SUCCESS; - - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - - /* NOTREACHED */ -} - -char *memcached_result_value(memcached_result_st *ptr) -{ - memcached_string_st *sptr= &ptr->value; - return memcached_string_value(sptr); -} - -size_t memcached_result_length(memcached_result_st *ptr) -{ - memcached_string_st *sptr= &ptr->value; - return memcached_string_length(sptr); -} - -static memcached_return_t binary_read_one_response(memcached_server_st *ptr, - char *buffer, size_t buffer_length, - memcached_result_st *result) -{ - protocol_binary_response_header header; - - unlikely (memcached_safe_read(ptr, &header.bytes, - sizeof(header.bytes)) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - - unlikely (header.response.magic != PROTOCOL_BINARY_RES) - return MEMCACHED_PROTOCOL_ERROR; - - /* - ** Convert the header to host local endian! - */ - header.response.keylen= ntohs(header.response.keylen); - header.response.status= ntohs(header.response.status); - header.response.bodylen= ntohl(header.response.bodylen); - header.response.cas= ntohll(header.response.cas); - uint32_t bodylen= header.response.bodylen; - - if (header.response.status == 0) - { - switch (header.response.opcode) - { - case PROTOCOL_BINARY_CMD_GETKQ: - /* - * We didn't increment the response counter for the GETKQ packet - * (only the final NOOP), so we need to increment the counter again. - */ - memcached_server_response_increment(ptr); - /* FALLTHROUGH */ - case PROTOCOL_BINARY_CMD_GETK: - { - uint16_t keylen= header.response.keylen; - memcached_result_reset(result); - result->cas= header.response.cas; - - if (memcached_safe_read(ptr, &result->flags, - sizeof (result->flags)) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - - result->flags= ntohl(result->flags); - bodylen -= header.response.extlen; - - result->key_length= keylen; - if (memcached_safe_read(ptr, result->key, keylen) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - - bodylen -= keylen; - if (memcached_string_check(&result->value, - bodylen) != MEMCACHED_SUCCESS) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - char *vptr= memcached_string_value(&result->value); - if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - - memcached_string_set_length(&result->value, bodylen); - } - break; - case PROTOCOL_BINARY_CMD_INCREMENT: - case PROTOCOL_BINARY_CMD_DECREMENT: - { - if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t)) - return MEMCACHED_PROTOCOL_ERROR; - - WATCHPOINT_ASSERT(bodylen == buffer_length); - uint64_t val; - if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - - val= ntohll(val); - memcpy(buffer, &val, sizeof(val)); - } - break; - case PROTOCOL_BINARY_CMD_VERSION: - { - memset(buffer, 0, buffer_length); - if (bodylen >= buffer_length) - /* not enough space in buffer.. should not happen... */ - return MEMCACHED_UNKNOWN_READ_FAILURE; - else if (memcached_safe_read(ptr, buffer, bodylen) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - break; - case PROTOCOL_BINARY_CMD_FLUSH: - case PROTOCOL_BINARY_CMD_QUIT: - case PROTOCOL_BINARY_CMD_SET: - case PROTOCOL_BINARY_CMD_ADD: - case PROTOCOL_BINARY_CMD_REPLACE: - case PROTOCOL_BINARY_CMD_APPEND: - case PROTOCOL_BINARY_CMD_PREPEND: - case PROTOCOL_BINARY_CMD_DELETE: - { - WATCHPOINT_ASSERT(bodylen == 0); - return MEMCACHED_SUCCESS; - } - case PROTOCOL_BINARY_CMD_NOOP: - { - WATCHPOINT_ASSERT(bodylen == 0); - return MEMCACHED_END; - } - case PROTOCOL_BINARY_CMD_STAT: - { - if (bodylen == 0) - return MEMCACHED_END; - else if (bodylen + 1 > buffer_length) - /* not enough space in buffer.. should not happen... */ - return MEMCACHED_UNKNOWN_READ_FAILURE; - else - { - size_t keylen= header.response.keylen; - memset(buffer, 0, buffer_length); - if (memcached_safe_read(ptr, buffer, keylen) != MEMCACHED_SUCCESS || - memcached_safe_read(ptr, buffer + keylen + 1, - bodylen - keylen) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - } - } - break; - default: - { - /* Command not implemented yet! */ - WATCHPOINT_ASSERT(0); - return MEMCACHED_PROTOCOL_ERROR; - } - } - } - else if (header.response.bodylen) - { - /* What should I do with the error message??? just discard it for now */ - char hole[SMALL_STRING_LEN]; - while (bodylen > 0) - { - size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen; - if (memcached_safe_read(ptr, hole, nr) != MEMCACHED_SUCCESS) - return MEMCACHED_UNKNOWN_READ_FAILURE; - bodylen-= (uint32_t) nr; - } - - /* This might be an error from one of the quiet commands.. if - * so, just throw it away and get the next one. What about creating - * a callback to the user with the error information? - */ - switch (header.response.opcode) - { - case PROTOCOL_BINARY_CMD_SETQ: - case PROTOCOL_BINARY_CMD_ADDQ: - case PROTOCOL_BINARY_CMD_REPLACEQ: - case PROTOCOL_BINARY_CMD_APPENDQ: - case PROTOCOL_BINARY_CMD_PREPENDQ: - return binary_read_one_response(ptr, buffer, buffer_length, result); - default: - break; - } - } - - memcached_return_t rc= MEMCACHED_SUCCESS; - unlikely(header.response.status != 0) - switch (header.response.status) - { - case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT: - rc= MEMCACHED_NOTFOUND; - break; - case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS: - rc= MEMCACHED_DATA_EXISTS; - break; - case PROTOCOL_BINARY_RESPONSE_NOT_STORED: - rc= MEMCACHED_NOTSTORED; - break; - case PROTOCOL_BINARY_RESPONSE_E2BIG: - rc= MEMCACHED_E2BIG; - break; - case PROTOCOL_BINARY_RESPONSE_ENOMEM: - rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - break; - case PROTOCOL_BINARY_RESPONSE_EINVAL: - case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: - default: - /* @todo fix the error mappings */ - rc= MEMCACHED_PROTOCOL_ERROR; - break; - } - - return rc; -} diff --git a/libmemcached/memcached_result.c b/libmemcached/memcached_result.c deleted file mode 100644 index 14a0774e..00000000 --- a/libmemcached/memcached_result.c +++ /dev/null @@ -1,81 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Functions to manipulate the result structure. - * - */ - -/* - memcached_result_st are used to internally represent the return values from - memcached. We use a structure so that long term as identifiers are added - to memcached we will be able to absorb new attributes without having - to addjust the entire API. -*/ -#include "common.h" - -memcached_result_st *memcached_result_create(memcached_st *memc, - memcached_result_st *ptr) -{ - WATCHPOINT_ASSERT(memc && memc->options.is_initialized); - - /* Saving malloc calls :) */ - if (ptr) - { - memset(ptr, 0, sizeof(memcached_result_st)); - } - else - { - ptr= memc->call_malloc(memc, sizeof(memcached_result_st)); - - if (ptr == NULL) - return NULL; - ptr->options.is_allocated= true; - } - - ptr->options.is_initialized= true; - - ptr->root= memc; - memcached_string_create(memc, &ptr->value, 0); - WATCHPOINT_ASSERT_INITIALIZED(&ptr->value); - WATCHPOINT_ASSERT(ptr->value.string == NULL); - - return ptr; -} - -void memcached_result_reset(memcached_result_st *ptr) -{ - ptr->key_length= 0; - memcached_string_reset(&ptr->value); - ptr->flags= 0; - ptr->cas= 0; - ptr->expiration= 0; -} - -/* - NOTE turn into macro -*/ -memcached_return_t memcached_result_set_value(memcached_result_st *ptr, const char *value, size_t length) -{ - return memcached_string_append(&ptr->value, value, length); -} - -void memcached_result_free(memcached_result_st *ptr) -{ - if (ptr == NULL) - return; - - memcached_string_free(&ptr->value); - - if (memcached_is_allocated(ptr)) - { - free(ptr); - } - else - { - ptr->options.is_initialized= false; - } -} diff --git a/libmemcached/memcached_result.h b/libmemcached/memcached_result.h deleted file mode 100644 index 4b0c12e5..00000000 --- a/libmemcached/memcached_result.h +++ /dev/null @@ -1,65 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Functions to manipulate the result structure. - * - */ - -#ifndef __MEMCACHED_RESULT_H__ -#define __MEMCACHED_RESULT_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -struct memcached_result_st { - struct { - bool is_allocated:1; - bool is_initialized:1; - } options; - uint32_t flags; - time_t expiration; - memcached_st *root; - size_t key_length; - uint64_t cas; - memcached_string_st value; - char key[MEMCACHED_MAX_KEY]; - /* Add result callback function */ -}; - -/* Result Struct */ -LIBMEMCACHED_API -void memcached_result_free(memcached_result_st *result); -LIBMEMCACHED_API -void memcached_result_reset(memcached_result_st *ptr); -LIBMEMCACHED_API -memcached_result_st *memcached_result_create(memcached_st *ptr, - memcached_result_st *result); -#define memcached_result_key_value(A) (A)->key -#define memcached_result_key_length(A) (A)->key_length -#define memcached_result_string_st(A) ((A)->value) -#ifdef FIX -#define memcached_result_value(A) memcached_string_value((A)->value) -#define memcached_result_length(A) memcached_string_length((A)->value) -#else -LIBMEMCACHED_API -char *memcached_result_value(memcached_result_st *ptr); -LIBMEMCACHED_API -size_t memcached_result_length(memcached_result_st *ptr); -#endif -#define memcached_result_flags(A) (A)->flags -#define memcached_result_cas(A) (A)->cas -LIBMEMCACHED_API -memcached_return_t memcached_result_set_value(memcached_result_st *ptr, const char *value, size_t length); -#define memcached_result_set_flags(A,B) (A)->flags=(B) -#define memcached_result_set_expiration(A,B) (A)->expiration=(B) - -#ifdef __cplusplus -} -#endif - -#endif /* __MEMCACHED_RESULT_H__ */ diff --git a/libmemcached/memcached_server.c b/libmemcached/memcached_server.c deleted file mode 100644 index 2cff36da..00000000 --- a/libmemcached/memcached_server.c +++ /dev/null @@ -1,179 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: String structure used for libmemcached. - * - */ - -/* - This is a partial implementation for fetching/creating memcached_server_st objects. -*/ -#include "common.h" - -memcached_server_st *memcached_server_create(memcached_st *memc, memcached_server_st *ptr) -{ - if (ptr == NULL) - { - ptr= (memcached_server_st *)calloc(1, sizeof(memcached_server_st)); - - if (!ptr) - return NULL; /* MEMCACHED_MEMORY_ALLOCATION_FAILURE */ - - ptr->options.is_allocated= true; - } - else - { - memset(ptr, 0, sizeof(memcached_server_st)); - } - - ptr->root= memc; - - return ptr; -} - -memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host, - const char *hostname, in_port_t port, - uint32_t weight, memcached_connection_t type) -{ - host= memcached_server_create(memc, host); - - if (host == NULL) - return NULL; - - strncpy(host->hostname, hostname, MEMCACHED_MAX_HOST_LENGTH - 1); - host->root= memc ? memc : NULL; - host->port= port; - host->weight= weight; - host->fd= -1; - host->type= type; - host->read_ptr= host->read_buffer; - if (memc) - host->next_retry= memc->retry_timeout; - if (type == MEMCACHED_CONNECTION_UDP) - { - host->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; - memcached_io_init_udp_header(host, 0); - } - - return host; -} - -void memcached_server_free(memcached_server_st *ptr) -{ - memcached_quit_server(ptr, 0); - - if (ptr->cached_server_error) - free(ptr->cached_server_error); - - if (ptr->address_info) - freeaddrinfo(ptr->address_info); - - - if (memcached_is_allocated(ptr)) - { - ptr->root->call_free(ptr->root, ptr); - } - else - { - memset(ptr, 0, sizeof(memcached_server_st)); - } -} - -/* - If we do not have a valid object to clone from, we toss an error. -*/ -memcached_server_st *memcached_server_clone(memcached_server_st *clone, memcached_server_st *ptr) -{ - memcached_server_st *rv= NULL; - - /* We just do a normal create if ptr is missing */ - if (ptr == NULL) - return NULL; - - rv = memcached_server_create_with(ptr->root, clone, - ptr->hostname, ptr->port, ptr->weight, - ptr->type); - if (rv != NULL) - { - rv->cached_errno= ptr->cached_errno; - if (ptr->cached_server_error) - rv->cached_server_error= strdup(ptr->cached_server_error); - } - - return rv; - -} - -memcached_return_t memcached_server_cursor(memcached_st *ptr, - memcached_server_fn *callback, - void *context, - uint32_t number_of_callbacks) -{ - unsigned int y; - - for (y= 0; y < ptr->number_of_hosts; y++) - { - unsigned int x; - - for (x= 0; x < number_of_callbacks; x++) - { - unsigned int iferror; - - iferror= (*callback[x])(ptr, &ptr->hosts[y], context); - - if (iferror) - continue; - } - } - - return MEMCACHED_SUCCESS; -} - -memcached_server_st *memcached_server_by_key(memcached_st *ptr, const char *key, size_t key_length, memcached_return_t *error) -{ - uint32_t server_key; - - *error= memcached_validate_key_length(key_length, - ptr->flags.binary_protocol); - unlikely (*error != MEMCACHED_SUCCESS) - return NULL; - - unlikely (ptr->number_of_hosts == 0) - { - *error= MEMCACHED_NO_SERVERS; - return NULL; - } - - if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - { - *error= MEMCACHED_BAD_KEY_PROVIDED; - return NULL; - } - - server_key= memcached_generate_hash(ptr, key, key_length); - - return memcached_server_clone(NULL, &ptr->hosts[server_key]); - -} - -const char *memcached_server_error(memcached_server_st *ptr) -{ - if (ptr) - return ptr->cached_server_error; - else - return NULL; -} - -void memcached_server_error_reset(memcached_server_st *ptr) -{ - ptr->cached_server_error[0]= 0; -} - -memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr) -{ - return ptr->last_disconnected_server; -} diff --git a/libmemcached/memcached_server.h b/libmemcached/memcached_server.h deleted file mode 100644 index f2d92dc1..00000000 --- a/libmemcached/memcached_server.h +++ /dev/null @@ -1,101 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: String structure used for libmemcached. - * - */ - -#ifndef __MEMCACHED_SERVER_H__ -#define __MEMCACHED_SERVER_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -struct memcached_server_st { - struct { - bool is_allocated:1; - } options; - bool sockaddr_inited; - uint16_t count; - uint32_t cursor_active; - in_port_t port; - int cached_errno; - int fd; - uint32_t io_bytes_sent; /* # bytes sent since last read */ - uint32_t server_failure_counter; - uint32_t weight; - uint8_t major_version; - uint8_t micro_version; - uint8_t minor_version; - memcached_connection_t type; - char *read_ptr; - char *cached_server_error; - size_t read_buffer_length; - size_t read_data_length; - size_t write_buffer_offset; - struct addrinfo *address_info; - time_t next_retry; - memcached_st *root; - uint64_t limit_maxbytes; - char read_buffer[MEMCACHED_MAX_BUFFER]; - char write_buffer[MEMCACHED_MAX_BUFFER]; - char hostname[MEMCACHED_MAX_HOST_LENGTH]; -}; - -#define memcached_server_count(A) (A)->number_of_hosts -#define memcached_server_name(A,B) (B).hostname -#define memcached_server_port(A,B) (B).port -#define memcached_server_list(A) (A)->hosts -#define memcached_server_response_count(A) (A)->cursor_active - -LIBMEMCACHED_API -memcached_return_t memcached_server_cursor(memcached_st *ptr, - memcached_server_fn *callback, - void *context, - uint32_t number_of_callbacks); - -LIBMEMCACHED_API -memcached_server_st *memcached_server_by_key(memcached_st *ptr, const char *key, - size_t key_length, memcached_return_t *error); - -LIBMEMCACHED_API -const char *memcached_server_error(memcached_server_st *ptr); - -LIBMEMCACHED_API -void memcached_server_error_reset(memcached_server_st *ptr); - -/* These should not currently be used by end users */ -/* TODO: Is the above comment valid? If so, how can we unit test these if they - * aren't exported. If not, we should remove the comment */ -LIBMEMCACHED_API -memcached_server_st *memcached_server_create(memcached_st *memc, memcached_server_st *ptr); - -LIBMEMCACHED_API -memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host, - const char *hostname, in_port_t port, - uint32_t weight, memcached_connection_t type); - -LIBMEMCACHED_API -void memcached_server_free(memcached_server_st *ptr); -LIBMEMCACHED_API -memcached_server_st *memcached_server_clone(memcached_server_st *clone, memcached_server_st *ptr); -LIBMEMCACHED_API -memcached_analysis_st *memcached_analyze(memcached_st *memc, memcached_stat_st *memc_stat, - memcached_return_t *error); - -LIBMEMCACHED_API -memcached_return_t memcached_server_remove(memcached_server_st *st_ptr); - -LIBMEMCACHED_API -memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr); - -#ifdef __cplusplus -} -#endif - -#endif /* __MEMCACHED_SERVER_H__ */ diff --git a/libmemcached/memcached_stats.c b/libmemcached/memcached_stats.c deleted file mode 100644 index e5f9a20f..00000000 --- a/libmemcached/memcached_stats.c +++ /dev/null @@ -1,456 +0,0 @@ -/* -*/ - -#include "common.h" - -static const char *memcached_stat_keys[] = { - "pid", - "uptime", - "time", - "version", - "pointer_size", - "rusage_user", - "rusage_system", - "curr_items", - "total_items", - "bytes", - "curr_connections", - "total_connections", - "connection_structures", - "cmd_get", - "cmd_set", - "get_hits", - "get_misses", - "evictions", - "bytes_read", - "bytes_written", - "limit_maxbytes", - "threads", - NULL -}; - - -static memcached_return_t set_data(memcached_stat_st *memc_stat, char *key, char *value) -{ - - if (strlen(key) < 1) - { - WATCHPOINT_STRING(key); - return MEMCACHED_UNKNOWN_STAT_KEY; - } - else if (!strcmp("pid", key)) - { - memc_stat->pid= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("uptime", key)) - { - memc_stat->uptime= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("time", key)) - { - memc_stat->time= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("version", key)) - { - memcpy(memc_stat->version, value, strlen(value)); - memc_stat->version[strlen(value)]= 0; - } - else if (!strcmp("pointer_size", key)) - { - memc_stat->pointer_size= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("rusage_user", key)) - { - char *walk_ptr; - for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); - *walk_ptr= 0; - walk_ptr++; - memc_stat->rusage_user_seconds= (uint32_t) strtol(value, (char **)NULL, 10); - memc_stat->rusage_user_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); - } - else if (!strcmp("rusage_system", key)) - { - char *walk_ptr; - for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); - *walk_ptr= 0; - walk_ptr++; - memc_stat->rusage_system_seconds= (uint32_t) strtol(value, (char **)NULL, 10); - memc_stat->rusage_system_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); - } - else if (!strcmp("curr_items", key)) - { - memc_stat->curr_items= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("total_items", key)) - { - memc_stat->total_items= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("bytes_read", key)) - { - memc_stat->bytes_read= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("bytes_written", key)) - { - memc_stat->bytes_written= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("bytes", key)) - { - memc_stat->bytes= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("curr_connections", key)) - { - memc_stat->curr_connections= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("total_connections", key)) - { - memc_stat->total_connections= (uint32_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("connection_structures", key)) - { - memc_stat->connection_structures= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!strcmp("cmd_get", key)) - { - memc_stat->cmd_get= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("cmd_set", key)) - { - memc_stat->cmd_set= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("get_hits", key)) - { - memc_stat->get_hits= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("get_misses", key)) - { - memc_stat->get_misses= (uint64_t)strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("evictions", key)) - { - memc_stat->evictions= (uint64_t)strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("limit_maxbytes", key)) - { - memc_stat->limit_maxbytes= (uint64_t) strtoll(value, (char **)NULL, 10); - } - else if (!strcmp("threads", key)) - { - memc_stat->threads= (uint32_t) strtol(value, (char **)NULL, 10); - } - else if (!(strcmp("delete_misses", key) == 0 ||/* New stats in the 1.3 beta */ - strcmp("delete_hits", key) == 0 ||/* Just swallow them for now.. */ - strcmp("incr_misses", key) == 0 || - strcmp("incr_hits", key) == 0 || - strcmp("decr_misses", key) == 0 || - strcmp("decr_hits", key) == 0 || - strcmp("cas_misses", key) == 0 || - strcmp("cas_hits", key) == 0 || - strcmp("cas_badval", key) == 0 || - strcmp("cmd_flush", key) == 0 || - strcmp("accepting_conns", key) == 0 || - strcmp("listen_disabled_num", key) == 0 || - strcmp("conn_yields", key) == 0)) - { - WATCHPOINT_STRING(key); - return MEMCACHED_UNKNOWN_STAT_KEY; - } - - return MEMCACHED_SUCCESS; -} - -char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat, - const char *key, memcached_return_t *error) -{ - char buffer[SMALL_STRING_LEN]; - int length; - char *ret; - - *error= MEMCACHED_SUCCESS; - - if (!memcmp("pid", key, strlen("pid"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pid); - else if (!memcmp("uptime", key, strlen("uptime"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->uptime); - else if (!memcmp("time", key, strlen("time"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->time); - else if (!memcmp("version", key, strlen("version"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%s", memc_stat->version); - else if (!memcmp("pointer_size", key, strlen("pointer_size"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pointer_size); - else if (!memcmp("rusage_user", key, strlen("rusage_user"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_user_seconds, memc_stat->rusage_user_microseconds); - else if (!memcmp("rusage_system", key, strlen("rusage_system"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_system_seconds, memc_stat->rusage_system_microseconds); - else if (!memcmp("curr_items", key, strlen("curr_items"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_items); - else if (!memcmp("total_items", key, strlen("total_items"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_items); - else if (!memcmp("curr_connections", key, strlen("curr_connections"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_connections); - else if (!memcmp("total_connections", key, strlen("total_connections"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_connections); - else if (!memcmp("connection_structures", key, strlen("connection_structures"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->connection_structures); - else if (!memcmp("cmd_get", key, strlen("cmd_get"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_get); - else if (!memcmp("cmd_set", key, strlen("cmd_set"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_set); - else if (!memcmp("get_hits", key, strlen("get_hits"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_hits); - else if (!memcmp("get_misses", key, strlen("get_misses"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_misses); - else if (!memcmp("evictions", key, strlen("evictions"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->evictions); - else if (!memcmp("bytes_read", key, strlen("bytes_read"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read); - else if (!memcmp("bytes_written", key, strlen("bytes_written"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written); - else if (!memcmp("bytes", key, strlen("bytes"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes); - else if (!memcmp("limit_maxbytes", key, strlen("limit_maxbytes"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes); - else if (!memcmp("threads", key, strlen("threads"))) - length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->threads); - else - { - *error= MEMCACHED_NOTFOUND; - return NULL; - } - - ret= ptr->call_malloc(ptr, (size_t) (length + 1)); - memcpy(ret, buffer, (size_t) length); - ret[length]= '\0'; - - return ret; -} - -static memcached_return_t binary_stats_fetch(memcached_st *ptr, - memcached_stat_st *memc_stat, - char *args, - unsigned int server_key) -{ - memcached_return_t rc; - - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - protocol_binary_request_stats request= {.bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - - if (args != NULL) - { - size_t len= strlen(args); - - rc= memcached_validate_key_length(len, true); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - request.message.header.request.keylen= htons((uint16_t)len); - request.message.header.request.bodylen= htonl((uint32_t) len); - - if ((memcached_do(&ptr->hosts[server_key], request.bytes, - sizeof(request.bytes), 0) != MEMCACHED_SUCCESS) || - (memcached_io_write(&ptr->hosts[server_key], args, len, 1) == -1)) - { - memcached_io_reset(&ptr->hosts[server_key]); - return MEMCACHED_WRITE_FAILURE; - } - } - else - { - if (memcached_do(&ptr->hosts[server_key], request.bytes, - sizeof(request.bytes), 1) != MEMCACHED_SUCCESS) - { - memcached_io_reset(&ptr->hosts[server_key]); - return MEMCACHED_WRITE_FAILURE; - } - } - - memcached_server_response_decrement(&ptr->hosts[server_key]); - do - { - rc= memcached_response(&ptr->hosts[server_key], buffer, - sizeof(buffer), NULL); - if (rc == MEMCACHED_END) - break; - - unlikely (rc != MEMCACHED_SUCCESS) - { - memcached_io_reset(&ptr->hosts[server_key]); - return rc; - } - - unlikely((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY) - { - WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); - WATCHPOINT_ASSERT(0); - } - } while (1); - - /* shit... memcached_response will decrement the counter, so I need to - ** reset it.. todo: look at this and try to find a better solution. - */ - ptr->hosts[server_key].cursor_active= 0; - - return MEMCACHED_SUCCESS; -} - -static memcached_return_t ascii_stats_fetch(memcached_st *ptr, - memcached_stat_st *memc_stat, - char *args, - unsigned int server_key) -{ - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - size_t send_length; - - if (args) - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "stats %s\r\n", args); - else - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "stats\r\n"); - - if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) - return MEMCACHED_WRITE_FAILURE; - - rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, 1); - if (rc != MEMCACHED_SUCCESS) - goto error; - - while (1) - { - rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - if (rc == MEMCACHED_STAT) - { - char *string_ptr, *end_ptr; - char *key, *value; - - string_ptr= buffer; - string_ptr+= 5; /* Move past STAT */ - for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); - key= string_ptr; - key[(size_t)(end_ptr-string_ptr)]= 0; - - string_ptr= end_ptr + 1; - for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++); - value= string_ptr; - value[(size_t)(end_ptr-string_ptr)]= 0; - string_ptr= end_ptr + 2; - unlikely((set_data(memc_stat, key, value)) == MEMCACHED_UNKNOWN_STAT_KEY) - { - WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); - WATCHPOINT_ASSERT(0); - } - } - else - break; - } - -error: - if (rc == MEMCACHED_END) - return MEMCACHED_SUCCESS; - else - return rc; -} - -memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_return_t *error) -{ - unsigned int x; - memcached_return_t rc; - memcached_stat_st *stats; - - unlikely (ptr->flags.use_udp) - { - *error= MEMCACHED_NOT_SUPPORTED; - return NULL; - } - - stats= ptr->call_calloc(ptr, ptr->number_of_hosts, sizeof(memcached_stat_st)); - - if (!stats) - { - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return NULL; - } - - rc= MEMCACHED_SUCCESS; - for (x= 0; x < ptr->number_of_hosts; x++) - { - memcached_return_t temp_return; - - if (ptr->flags.binary_protocol) - temp_return= binary_stats_fetch(ptr, stats + x, args, x); - else - temp_return= ascii_stats_fetch(ptr, stats + x, args, x); - - if (temp_return != MEMCACHED_SUCCESS) - rc= MEMCACHED_SOME_ERRORS; - } - - *error= rc; - return stats; -} - -memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args, - const char *hostname, in_port_t port) -{ - memcached_return_t rc; - memcached_st memc; - memcached_st *memc_ptr; - - memc_ptr= memcached_create(&memc); - WATCHPOINT_ASSERT(memc_ptr); - - memcached_server_add(&memc, hostname, port); - - if (memc.flags.binary_protocol) - rc= binary_stats_fetch(&memc, memc_stat, args, 0); - else - rc= ascii_stats_fetch(&memc, memc_stat, args, 0); - - memcached_free(&memc); - - return rc; -} - -/* - We make a copy of the keys since at some point in the not so distant future - we will add support for "found" keys. -*/ -char ** memcached_stat_get_keys(memcached_st *ptr, memcached_stat_st *memc_stat, - memcached_return_t *error) -{ - (void) memc_stat; - char **list; - size_t length= sizeof(memcached_stat_keys); - - list= ptr->call_malloc(ptr, length); - - if (!list) - { - *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; - return NULL; - } - - memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys)); - - *error= MEMCACHED_SUCCESS; - - return list; -} - -void memcached_stat_free(memcached_st *ptr, memcached_stat_st *memc_stat) -{ - if (memc_stat == NULL) - { - WATCHPOINT_ASSERT(0); /* Be polite, but when debugging catch this as an error */ - return; - } - - if (ptr) - ptr->call_free(ptr, memc_stat); - else - free(memc_stat); -} diff --git a/libmemcached/memcached_storage.c b/libmemcached/memcached_storage.c deleted file mode 100644 index c6a556f9..00000000 --- a/libmemcached/memcached_storage.c +++ /dev/null @@ -1,520 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Storage related functions, aka set, replace,.. - * - */ - -#include "common.h" -#include "memcached_io.h" - -typedef enum { - SET_OP, - REPLACE_OP, - ADD_OP, - PREPEND_OP, - APPEND_OP, - CAS_OP, -} memcached_storage_action_t; - -/* Inline this */ -static inline const char *storage_op_string(memcached_storage_action_t verb) -{ - switch (verb) - { - case SET_OP: - return "set "; - case REPLACE_OP: - return "replace "; - case ADD_OP: - return "add "; - case PREPEND_OP: - return "prepend "; - case APPEND_OP: - return "append "; - case CAS_OP: - return "cas "; - default: - return "tosserror"; /* This is impossible, fixes issue for compiler warning in VisualStudio */ - } - - /* NOTREACHED */ -} - -static memcached_return_t memcached_send_binary(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char *key, - size_t key_length, - const char *value, - size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas, - memcached_storage_action_t verb); - -static inline memcached_return_t memcached_send(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas, - memcached_storage_action_t verb) -{ - char to_write; - size_t write_length; - ssize_t sent_length; - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - unsigned int server_key; - - WATCHPOINT_ASSERT(!(value == NULL && value_length > 0)); - - rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); - unlikely (rc != MEMCACHED_SUCCESS) - return rc; - - unlikely (ptr->number_of_hosts == 0) - return MEMCACHED_NO_SERVERS; - - if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) - return MEMCACHED_BAD_KEY_PROVIDED; - - if (ptr->flags.binary_protocol) - { - return memcached_send_binary(ptr, master_key, master_key_length, - key, key_length, - value, value_length, expiration, - flags, cas, verb); - } - - server_key= memcached_generate_hash(ptr, master_key, master_key_length); - - if (cas) - { - write_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%s %s%.*s %u %llu %zu %llu%s\r\n", - storage_op_string(verb), - ptr->prefix_key, - (int)key_length, key, flags, - (unsigned long long)expiration, value_length, - (unsigned long long)cas, - (ptr->flags.no_reply) ? " noreply" : ""); - } - else - { - char *buffer_ptr= buffer; - const char *command= storage_op_string(verb); - - /* Copy in the command, no space needed, we handle that in the command function*/ - memcpy(buffer_ptr, command, strlen(command)); - - /* Copy in the key prefix, switch to the buffer_ptr */ - buffer_ptr= memcpy(buffer_ptr + strlen(command) , ptr->prefix_key, strlen(ptr->prefix_key)); - - /* Copy in the key, adjust point if a key prefix was used. */ - buffer_ptr= memcpy(buffer_ptr + (ptr->prefix_key ? strlen(ptr->prefix_key) : 0), - key, key_length); - buffer_ptr+= key_length; - buffer_ptr[0]= ' '; - buffer_ptr++; - - write_length= (size_t)(buffer_ptr - buffer); - write_length+= (size_t) snprintf(buffer_ptr, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%u %llu %zu%s\r\n", - flags, - (unsigned long long)expiration, value_length, - ptr->flags.no_reply ? " noreply" : ""); - } - - if (ptr->flags.use_udp && ptr->flags.buffer_requests) - { - size_t cmd_size= write_length + value_length + 2; - if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - return MEMCACHED_WRITE_FAILURE; - if (cmd_size + ptr->hosts[server_key].write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - memcached_io_write(&ptr->hosts[server_key], NULL, 0, 1); - } - - if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) - { - rc= MEMCACHED_WRITE_FAILURE; - goto error; - } - - /* Send command header */ - rc= memcached_do(&ptr->hosts[server_key], buffer, write_length, 0); - if (rc != MEMCACHED_SUCCESS) - goto error; - - /* Send command body */ - if ((sent_length= memcached_io_write(&ptr->hosts[server_key], value, value_length, 0)) == -1) - { - rc= MEMCACHED_WRITE_FAILURE; - goto error; - } - - if (ptr->flags.buffer_requests && verb == SET_OP) - { - to_write= 0; - } - else - { - to_write= 1; - } - - if ((sent_length= memcached_io_write(&ptr->hosts[server_key], "\r\n", 2, to_write)) == -1) - { - rc= MEMCACHED_WRITE_FAILURE; - goto error; - } - - if (ptr->flags.no_reply) - return (to_write == 0) ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS; - - if (to_write == 0) - return MEMCACHED_BUFFERED; - - rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - - if (rc == MEMCACHED_STORED) - return MEMCACHED_SUCCESS; - else - return rc; - -error: - memcached_io_reset(&ptr->hosts[server_key]); - - return rc; -} - - -memcached_return_t memcached_set(memcached_st *ptr, const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_SET_START(); - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, SET_OP); - LIBMEMCACHED_MEMCACHED_SET_END(); - return rc; -} - -memcached_return_t memcached_add(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_ADD_START(); - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, ADD_OP); - LIBMEMCACHED_MEMCACHED_ADD_END(); - return rc; -} - -memcached_return_t memcached_replace(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_REPLACE_START(); - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, REPLACE_OP); - LIBMEMCACHED_MEMCACHED_REPLACE_END(); - return rc; -} - -memcached_return_t memcached_prepend(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, PREPEND_OP); - return rc; -} - -memcached_return_t memcached_append(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, 0, APPEND_OP); - return rc; -} - -memcached_return_t memcached_cas(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas) -{ - memcached_return_t rc; - rc= memcached_send(ptr, key, key_length, - key, key_length, value, value_length, - expiration, flags, cas, CAS_OP); - return rc; -} - -memcached_return_t memcached_set_by_key(memcached_st *ptr, - const char *master_key __attribute__((unused)), - size_t master_key_length __attribute__((unused)), - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_SET_START(); - rc= memcached_send(ptr, master_key, master_key_length, - key, key_length, value, value_length, - expiration, flags, 0, SET_OP); - LIBMEMCACHED_MEMCACHED_SET_END(); - return rc; -} - -memcached_return_t memcached_add_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_ADD_START(); - rc= memcached_send(ptr, master_key, master_key_length, - key, key_length, value, value_length, - expiration, flags, 0, ADD_OP); - LIBMEMCACHED_MEMCACHED_ADD_END(); - return rc; -} - -memcached_return_t memcached_replace_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - LIBMEMCACHED_MEMCACHED_REPLACE_START(); - rc= memcached_send(ptr, master_key, master_key_length, - key, key_length, value, value_length, - expiration, flags, 0, REPLACE_OP); - LIBMEMCACHED_MEMCACHED_REPLACE_END(); - return rc; -} - -memcached_return_t memcached_prepend_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, master_key, master_key_length, - key, key_length, value, value_length, - expiration, flags, 0, PREPEND_OP); - return rc; -} - -memcached_return_t memcached_append_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags) -{ - memcached_return_t rc; - rc= memcached_send(ptr, master_key, master_key_length, - key, key_length, value, value_length, - expiration, flags, 0, APPEND_OP); - return rc; -} - -memcached_return_t memcached_cas_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas) -{ - memcached_return_t rc; - rc= memcached_send(ptr, master_key, master_key_length, - key, key_length, value, value_length, - expiration, flags, cas, CAS_OP); - return rc; -} - -static inline uint8_t get_com_code(memcached_storage_action_t verb, bool noreply) -{ - /* 0 isn't a value we want, but GCC 4.2 seems to think ret can otherwise - * be used uninitialized in this function. FAIL */ - uint8_t ret= 0; - - if (noreply) - switch (verb) - { - case SET_OP: - ret=PROTOCOL_BINARY_CMD_SETQ; - break; - case ADD_OP: - ret=PROTOCOL_BINARY_CMD_ADDQ; - break; - case CAS_OP: /* FALLTHROUGH */ - case REPLACE_OP: - ret=PROTOCOL_BINARY_CMD_REPLACEQ; - break; - case APPEND_OP: - ret=PROTOCOL_BINARY_CMD_APPENDQ; - break; - case PREPEND_OP: - ret=PROTOCOL_BINARY_CMD_PREPENDQ; - break; - default: - WATCHPOINT_ASSERT(verb); - break; - } - else - switch (verb) - { - case SET_OP: - ret=PROTOCOL_BINARY_CMD_SET; - break; - case ADD_OP: - ret=PROTOCOL_BINARY_CMD_ADD; - break; - case CAS_OP: /* FALLTHROUGH */ - case REPLACE_OP: - ret=PROTOCOL_BINARY_CMD_REPLACE; - break; - case APPEND_OP: - ret=PROTOCOL_BINARY_CMD_APPEND; - break; - case PREPEND_OP: - ret=PROTOCOL_BINARY_CMD_PREPEND; - break; - default: - WATCHPOINT_ASSERT(verb); - break; - } - - return ret; -} - - - -static memcached_return_t memcached_send_binary(memcached_st *ptr, - const char *master_key, - size_t master_key_length, - const char *key, - size_t key_length, - const char *value, - size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas, - memcached_storage_action_t verb) -{ - uint8_t flush; - protocol_binary_request_set request= {.bytes= {0}}; - size_t send_length= sizeof(request.bytes); - uint32_t server_key= memcached_generate_hash(ptr, master_key, - master_key_length); - memcached_server_st *server= &ptr->hosts[server_key]; - bool noreply= server->root->flags.no_reply; - - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= get_com_code(verb, noreply); - request.message.header.request.keylen= htons((uint16_t)key_length); - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - if (verb == APPEND_OP || verb == PREPEND_OP) - send_length -= 8; /* append & prepend does not contain extras! */ - else - { - request.message.header.request.extlen= 8; - request.message.body.flags= htonl(flags); - request.message.body.expiration= htonl((uint32_t)expiration); - } - - request.message.header.request.bodylen= htonl((uint32_t) (key_length + value_length + - request.message.header.request.extlen)); - - if (cas) - request.message.header.request.cas= htonll(cas); - - flush= (uint8_t) ((server->root->flags.buffer_requests && verb == SET_OP) ? 0 : 1); - - if (server->root->flags.use_udp && !flush) - { - size_t cmd_size= send_length + key_length + value_length; - - if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) - return MEMCACHED_WRITE_FAILURE; - if (cmd_size + server->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) - memcached_io_write(server,NULL,0, 1); - } - - /* write the header */ - if ((memcached_do(server, (const char*)request.bytes, send_length, 0) != MEMCACHED_SUCCESS) || - (memcached_io_write(server, key, key_length, 0) == -1) || - (memcached_io_write(server, value, value_length, (char) flush) == -1)) - { - memcached_io_reset(server); - return MEMCACHED_WRITE_FAILURE; - } - - unlikely (verb == SET_OP && ptr->number_of_replicas > 0) - { - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ; - - for (uint32_t x= 0; x < ptr->number_of_replicas; x++) - { - ++server_key; - if (server_key == ptr->number_of_hosts) - server_key= 0; - - memcached_server_st *srv= &ptr->hosts[server_key]; - if ((memcached_do(srv, (const char*)request.bytes, - send_length, 0) != MEMCACHED_SUCCESS) || - (memcached_io_write(srv, key, key_length, 0) == -1) || - (memcached_io_write(srv, value, value_length, (char) flush) == -1)) - memcached_io_reset(srv); - else - memcached_server_response_decrement(srv); - } - } - - if (flush == 0) - return MEMCACHED_BUFFERED; - - if (noreply) - return MEMCACHED_SUCCESS; - - return memcached_response(server, NULL, 0, NULL); -} - diff --git a/libmemcached/memcached_storage.h b/libmemcached/memcached_storage.h deleted file mode 100644 index d4f4995c..00000000 --- a/libmemcached/memcached_storage.h +++ /dev/null @@ -1,110 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Storage related functions, aka set, replace,.. - * - */ - -#ifndef __MEMCACHED_STORAGE_H__ -#define __MEMCACHED_STORAGE_H__ - -#include "libmemcached/memcached_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* All of the functions for adding data to the server */ -LIBMEMCACHED_API -memcached_return_t memcached_set(memcached_st *ptr, const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); -LIBMEMCACHED_API -memcached_return_t memcached_add(memcached_st *ptr, const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); -LIBMEMCACHED_API -memcached_return_t memcached_replace(memcached_st *ptr, const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); -LIBMEMCACHED_API -memcached_return_t memcached_append(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); -LIBMEMCACHED_API -memcached_return_t memcached_prepend(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); -LIBMEMCACHED_API -memcached_return_t memcached_cas(memcached_st *ptr, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas); - -LIBMEMCACHED_API -memcached_return_t memcached_set_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); - -LIBMEMCACHED_API -memcached_return_t memcached_add_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); - -LIBMEMCACHED_API -memcached_return_t memcached_replace_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); - -LIBMEMCACHED_API -memcached_return_t memcached_prepend_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); - -LIBMEMCACHED_API -memcached_return_t memcached_append_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags); - -LIBMEMCACHED_API -memcached_return_t memcached_cas_by_key(memcached_st *ptr, - const char *master_key, size_t master_key_length, - const char *key, size_t key_length, - const char *value, size_t value_length, - time_t expiration, - uint32_t flags, - uint64_t cas); - -#ifdef __cplusplus -} -#endif - -#endif /* __MEMCACHED_STORAGE_H__ */ diff --git a/libmemcached/memcached_strerror.c b/libmemcached/memcached_strerror.c deleted file mode 100644 index f1d651c9..00000000 --- a/libmemcached/memcached_strerror.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "common.h" - -const char *memcached_strerror(memcached_st *ptr __attribute__((unused)), memcached_return_t rc) -{ - switch (rc) - { - case MEMCACHED_SUCCESS: - return "SUCCESS"; - case MEMCACHED_FAILURE: - return "FAILURE"; - case MEMCACHED_HOST_LOOKUP_FAILURE: - return "HOSTNAME LOOKUP FAILURE"; - case MEMCACHED_CONNECTION_FAILURE: - return "CONNECTION FAILURE"; - case MEMCACHED_CONNECTION_BIND_FAILURE: - return "CONNECTION BIND FAILURE"; - case MEMCACHED_READ_FAILURE: - return "READ FAILURE"; - case MEMCACHED_UNKNOWN_READ_FAILURE: - return "UNKNOWN READ FAILURE"; - case MEMCACHED_PROTOCOL_ERROR: - return "PROTOCOL ERROR"; - case MEMCACHED_CLIENT_ERROR: - return "CLIENT ERROR"; - case MEMCACHED_SERVER_ERROR: - return "SERVER ERROR"; - case MEMCACHED_WRITE_FAILURE: - return "WRITE FAILURE"; - case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: - return "CONNECTION SOCKET CREATE FAILURE"; - case MEMCACHED_DATA_EXISTS: - return "CONNECTION DATA EXISTS"; - case MEMCACHED_DATA_DOES_NOT_EXIST: - return "CONNECTION DATA DOES NOT EXIST"; - case MEMCACHED_NOTSTORED: - return "NOT STORED"; - case MEMCACHED_STORED: - return "STORED"; - case MEMCACHED_NOTFOUND: - return "NOT FOUND"; - case MEMCACHED_MEMORY_ALLOCATION_FAILURE: - return "MEMORY ALLOCATION FAILURE"; - case MEMCACHED_PARTIAL_READ: - return "PARTIAL READ"; - case MEMCACHED_SOME_ERRORS: - return "SOME ERRORS WERE REPORTED"; - case MEMCACHED_NO_SERVERS: - return "NO SERVERS DEFINED"; - case MEMCACHED_END: - return "SERVER END"; - case MEMCACHED_DELETED: - return "SERVER DELETE"; - case MEMCACHED_VALUE: - return "SERVER VALUE"; - case MEMCACHED_STAT: - return "STAT VALUE"; - case MEMCACHED_ITEM: - return "ITEM VALUE"; - case MEMCACHED_ERRNO: - return "SYSTEM ERROR"; - case MEMCACHED_FAIL_UNIX_SOCKET: - return "COULD NOT OPEN UNIX SOCKET"; - case MEMCACHED_NOT_SUPPORTED: - return "ACTION NOT SUPPORTED"; - case MEMCACHED_FETCH_NOTFINISHED: - return "FETCH WAS NOT COMPLETED"; - case MEMCACHED_NO_KEY_PROVIDED: - return "A KEY LENGTH OF ZERO WAS PROVIDED"; - case MEMCACHED_BUFFERED: - return "ACTION QUEUED"; - case MEMCACHED_TIMEOUT: - return "A TIMEOUT OCCURRED"; - case MEMCACHED_BAD_KEY_PROVIDED: - return "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE"; - case MEMCACHED_INVALID_HOST_PROTOCOL: - return "THE HOST TRANSPORT PROTOCOL DOES NOT MATCH THAT OF THE CLIENT"; - case MEMCACHED_SERVER_MARKED_DEAD: - return "SERVER IS MARKED DEAD"; - case MEMCACHED_UNKNOWN_STAT_KEY: - return "ENCOUNTERED AN UNKNOWN STAT KEY"; - case MEMCACHED_E2BIG: - return "ITEM TOO BIG"; - case MEMCACHED_INVALID_ARGUMENTS: - return "INVALID ARGUMENTS"; - case MEMCACHED_MAXIMUM_RETURN: - return "Gibberish returned!"; - default: - return "Gibberish returned!"; - } -} diff --git a/libmemcached/memcached_string.c b/libmemcached/memcached_string.c deleted file mode 100644 index 838ca416..00000000 --- a/libmemcached/memcached_string.c +++ /dev/null @@ -1,171 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: String structure used for libmemcached. - * - */ - -#include "common.h" - -inline static memcached_return_t _string_check(memcached_string_st *string, size_t need) -{ - if (need && need > (size_t)(string->current_size - (size_t)(string->end - string->string))) - { - size_t current_offset= (size_t) (string->end - string->string); - char *new_value; - size_t adjust; - size_t new_size; - - /* This is the block multiplier. To keep it larger and surive division errors we must round it up */ - adjust= (need - (size_t)(string->current_size - (size_t)(string->end - string->string))) / string->block_size; - adjust++; - - new_size= sizeof(char) * (size_t)((adjust * string->block_size) + string->current_size); - /* Test for overflow */ - if (new_size < need) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - new_value= string->root->call_realloc(string->root, string->string, new_size); - - if (new_value == NULL) - return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - - string->string= new_value; - string->end= string->string + current_offset; - - string->current_size+= (string->block_size * adjust); - } - - return MEMCACHED_SUCCESS; -} - -memcached_string_st *memcached_string_create(memcached_st *memc, memcached_string_st *string, size_t initial_size) -{ - memcached_return_t rc; - - /* Saving malloc calls :) */ - if (string) - { - WATCHPOINT_ASSERT(memc->options.is_safe && string->options.is_initialized == false); - - memset(string, 0, sizeof(memcached_string_st)); - } - else - { - string= memc->call_calloc(memc, 1, sizeof(memcached_string_st)); - - if (string == NULL) - { - return NULL; - } - - string->options.is_allocated= true; - } - string->block_size= MEMCACHED_BLOCK_SIZE; - string->root= memc; - - rc= _string_check(string, initial_size); - if (rc != MEMCACHED_SUCCESS) - { - memc->call_free(memc, string); - return NULL; - } - - string->options.is_initialized= true; - - WATCHPOINT_ASSERT(string->string == string->end); - - return string; -} - -memcached_return_t memcached_string_append_character(memcached_string_st *string, - char character) -{ - memcached_return_t rc; - - rc= _string_check(string, 1); - - if (rc != MEMCACHED_SUCCESS) - return rc; - - *string->end= character; - string->end++; - - return MEMCACHED_SUCCESS; -} - -memcached_return_t memcached_string_append(memcached_string_st *string, - const char *value, size_t length) -{ - memcached_return_t rc; - - rc= _string_check(string, length); - - if (rc != MEMCACHED_SUCCESS) - return rc; - - WATCHPOINT_ASSERT(length <= string->current_size); - WATCHPOINT_ASSERT(string->string); - WATCHPOINT_ASSERT(string->end >= string->string); - - memcpy(string->end, value, length); - string->end+= length; - - return MEMCACHED_SUCCESS; -} - -char *memcached_string_c_copy(memcached_string_st *string) -{ - char *c_ptr; - - if (memcached_string_length(string) == 0) - return NULL; - - c_ptr= string->root->call_malloc(string->root, (memcached_string_length(string)+1) * sizeof(char)); - - if (c_ptr == NULL) - return NULL; - - memcpy(c_ptr, memcached_string_value(string), memcached_string_length(string)); - c_ptr[memcached_string_length(string)]= 0; - - return c_ptr; -} - -memcached_return_t memcached_string_reset(memcached_string_st *string) -{ - string->end= string->string; - - return MEMCACHED_SUCCESS; -} - -void memcached_string_free(memcached_string_st *ptr) -{ - if (ptr == NULL) - return; - - if (ptr->string) - { - ptr->root->call_free(ptr->root, ptr->string); - } - - if (memcached_is_allocated(ptr)) - { - ptr->root->call_free(ptr->root, ptr); - } - else - { - ptr->options.is_initialized= false; - memset(ptr, 0, sizeof(memcached_string_st)); - } -} - -memcached_return_t memcached_string_check(memcached_string_st *string, size_t need) -{ - return _string_check(string, need); -} - diff --git a/libmemcached/memcached_string.h b/libmemcached/memcached_string.h deleted file mode 100644 index d93bc0d2..00000000 --- a/libmemcached/memcached_string.h +++ /dev/null @@ -1,62 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: String structure used for libmemcached. - * - */ - -#ifndef __MEMCACHED_STRING_H__ -#define __MEMCACHED_STRING_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -struct memcached_string_st { - memcached_st *root; - char *end; - char *string; - size_t current_size; - size_t block_size; - struct { - bool is_allocated:1; - bool is_initialized:1; - } options; -}; - -#define memcached_string_length(A) (size_t)((A)->end - (A)->string) -#define memcached_string_set_length(A, B) (A)->end= (A)->string + B -#define memcached_string_size(A) (A)->current_size -#define memcached_string_value(A) (A)->string - -LIBMEMCACHED_API -memcached_string_st *memcached_string_create(memcached_st *ptr, - memcached_string_st *string, - size_t initial_size); -LIBMEMCACHED_API -memcached_return_t memcached_string_check(memcached_string_st *string, size_t need); - -LIBMEMCACHED_API -char *memcached_string_c_copy(memcached_string_st *string); - -LIBMEMCACHED_API -memcached_return_t memcached_string_append_character(memcached_string_st *string, - char character); -LIBMEMCACHED_API -memcached_return_t memcached_string_append(memcached_string_st *string, - const char *value, size_t length); -LIBMEMCACHED_API -memcached_return_t memcached_string_reset(memcached_string_st *string); - -LIBMEMCACHED_API -void memcached_string_free(memcached_string_st *string); - -#ifdef __cplusplus -} -#endif - -#endif /* __MEMCACHED_STRING_H__ */ diff --git a/libmemcached/memcached_types.h b/libmemcached/memcached_types.h deleted file mode 100644 index 58f0887a..00000000 --- a/libmemcached/memcached_types.h +++ /dev/null @@ -1,76 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Types for libmemcached - * - */ - -#ifndef LIBMEMCACHED_MEMCACHED_TYPES_H -#define LIBMEMCACHED_MEMCACHED_TYPES_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct memcached_st memcached_st; -typedef struct memcached_stat_st memcached_stat_st; -typedef struct memcached_analysis_st memcached_analysis_st; -typedef struct memcached_result_st memcached_result_st; -typedef struct memcached_string_st memcached_string_st; -typedef struct memcached_server_st memcached_server_st; -typedef struct memcached_continuum_item_st memcached_continuum_item_st; -typedef memcached_return_t (*memcached_clone_fn)(memcached_st *parent, memcached_st *clone); -typedef memcached_return_t (*memcached_cleanup_fn)(memcached_st *ptr); -typedef void (*memcached_free_fn)(memcached_st *ptr, void *mem); -typedef void *(*memcached_malloc_fn)(memcached_st *ptr, const size_t size); -typedef void *(*memcached_realloc_fn)(memcached_st *ptr, void *mem, const size_t size); -typedef void *(*memcached_calloc_fn)(memcached_st *ptr, size_t nelem, const size_t elsize); -typedef memcached_return_t (*memcached_execute_fn)(memcached_st *ptr, memcached_result_st *result, void *context); -typedef memcached_return_t (*memcached_server_fn)(memcached_st *ptr, memcached_server_st *server, void *context); -typedef memcached_return_t (*memcached_trigger_key_fn)(memcached_st *ptr, - const char *key, size_t key_length, - memcached_result_st *result); -typedef memcached_return_t (*memcached_trigger_delete_key_fn)(memcached_st *ptr, - const char *key, size_t key_length); - -typedef memcached_return_t (*memcached_dump_fn)(memcached_st *ptr, - const char *key, size_t key_length, void *context); - -typedef struct { - memcached_execute_fn *callback; - void *context; - uint32_t number_of_callback; -} memcached_callback_st; - - -/** - @note The following definitions are just here for backwards compatibility. -*/ -typedef memcached_return_t memcached_return; -typedef memcached_server_distribution_t memcached_server_distribution; -typedef memcached_behavior_t memcached_behavior; -typedef memcached_callback_t memcached_callback; -typedef memcached_hash_t memcached_hash; -typedef memcached_connection_t memcached_connection; -typedef memcached_clone_fn memcached_clone_func; -typedef memcached_cleanup_fn memcached_cleanup_func; -typedef memcached_free_fn memcached_free_function; -typedef memcached_malloc_fn memcached_malloc_function; -typedef memcached_realloc_fn memcached_realloc_function; -typedef memcached_calloc_fn memcached_calloc_function; -typedef memcached_execute_fn memcached_execute_function; -typedef memcached_server_fn memcached_server_function; -typedef memcached_trigger_key_fn memcached_trigger_key; -typedef memcached_trigger_delete_key_fn memcached_trigger_delete_key; -typedef memcached_dump_fn memcached_dump_func; - - -#ifdef __cplusplus -} -#endif - -#endif /* LIBMEMCACHED_MEMCACHED_TYPES_H */ diff --git a/libmemcached/memcached_util.h b/libmemcached/memcached_util.h index f06804b4..c1a719d6 100644 --- a/libmemcached/memcached_util.h +++ b/libmemcached/memcached_util.h @@ -15,6 +15,6 @@ #ifndef MEMCACHED_UTIL_H #define MEMCACHED_UTIL_H -#include +#include #endif /* MEMCACHED_UTIL_H */ diff --git a/libmemcached/memcached_verbosity.c b/libmemcached/memcached_verbosity.c deleted file mode 100644 index 3d34880d..00000000 --- a/libmemcached/memcached_verbosity.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "common.h" - -memcached_return_t memcached_verbosity(memcached_st *ptr, unsigned int verbosity) -{ - unsigned int x; - size_t send_length; - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - - send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "verbosity %u\r\n", verbosity); - unlikely (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) - return MEMCACHED_WRITE_FAILURE; - - rc= MEMCACHED_SUCCESS; - for (x= 0; x < ptr->number_of_hosts; x++) - { - memcached_return_t rrc; - - rrc= memcached_do(&ptr->hosts[x], buffer, send_length, 1); - if (rrc != MEMCACHED_SUCCESS) - { - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - unlikely (ptr->flags.use_udp) - continue; - - rrc= memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - if (rrc != MEMCACHED_SUCCESS) - rc= MEMCACHED_SOME_ERRORS; - } - - return rc; -} diff --git a/libmemcached/memcached_version.c b/libmemcached/memcached_version.c deleted file mode 100644 index db2419cf..00000000 --- a/libmemcached/memcached_version.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "common.h" - -const char * memcached_lib_version(void) -{ - return LIBMEMCACHED_VERSION_STRING; -} - -static inline memcached_return_t memcached_version_binary(memcached_st *ptr); -static inline memcached_return_t memcached_version_textual(memcached_st *ptr); - -memcached_return_t memcached_version(memcached_st *ptr) -{ - if (ptr->flags.use_udp) - return MEMCACHED_NOT_SUPPORTED; - - if (ptr->flags.binary_protocol) - return memcached_version_binary(ptr); - else - return memcached_version_textual(ptr); -} - -static inline memcached_return_t memcached_version_textual(memcached_st *ptr) -{ - unsigned int x; - size_t send_length; - memcached_return_t rc; - char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - char *response_ptr; - const char *command= "version\r\n"; - - send_length= strlen(command); - - rc= MEMCACHED_SUCCESS; - for (x= 0; x < ptr->number_of_hosts; x++) - { - memcached_return_t rrc; - - rrc= memcached_do(&ptr->hosts[x], command, send_length, 1); - if (rrc != MEMCACHED_SUCCESS) - { - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - rrc= memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); - if (rrc != MEMCACHED_SUCCESS) - { - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - /* Find the space, and then move one past it to copy version */ - response_ptr= index(buffer, ' '); - response_ptr++; - - ptr->hosts[x].major_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); - response_ptr= index(response_ptr, '.'); - response_ptr++; - ptr->hosts[x].minor_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); - response_ptr= index(response_ptr, '.'); - response_ptr++; - ptr->hosts[x].micro_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); - } - - return rc; -} - -static inline memcached_return_t memcached_version_binary(memcached_st *ptr) -{ - memcached_return_t rc; - unsigned int x; - protocol_binary_request_version request= { .bytes= {0}}; - request.message.header.request.magic= PROTOCOL_BINARY_REQ; - request.message.header.request.opcode= PROTOCOL_BINARY_CMD_VERSION; - request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; - - rc= MEMCACHED_SUCCESS; - for (x= 0; x < ptr->number_of_hosts; x++) - { - memcached_return_t rrc; - - rrc= memcached_do(&ptr->hosts[x], request.bytes, sizeof(request.bytes), 1); - if (rrc != MEMCACHED_SUCCESS) - { - memcached_io_reset(&ptr->hosts[x]); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - } - - for (x= 0; x < ptr->number_of_hosts; x++) - if (memcached_server_response_count(&ptr->hosts[x]) > 0) - { - memcached_return_t rrc; - char buffer[32]; - char *p; - - rrc= memcached_response(&ptr->hosts[x], buffer, sizeof(buffer), NULL); - if (rrc != MEMCACHED_SUCCESS) - { - memcached_io_reset(&ptr->hosts[x]); - rc= MEMCACHED_SOME_ERRORS; - continue; - } - - ptr->hosts[x].major_version= (uint8_t)strtol(buffer, &p, 10); - ptr->hosts[x].minor_version= (uint8_t)strtol(p + 1, &p, 10); - ptr->hosts[x].micro_version= (uint8_t)strtol(p + 1, NULL, 10); - } - - return rc; -} diff --git a/libmemcached/memcached_watchpoint.h b/libmemcached/memcached_watchpoint.h deleted file mode 100644 index d8c593ec..00000000 --- a/libmemcached/memcached_watchpoint.h +++ /dev/null @@ -1,45 +0,0 @@ -/* LibMemcached - * Copyright (C) 2006-2009 Brian Aker - * All rights reserved. - * - * Use and distribution licensed under the BSD license. See - * the COPYING file in the parent directory for full text. - * - * Summary: Localized copy of WATCHPOINT debug symbols - * - */ - -#ifndef LIBMEMCACHED_MEMCACHED_WATCHPOINT_H -#define LIBMEMCACHED_MEMCACHED_WATCHPOINT_H - -/* Some personal debugging functions */ -#if defined(DEBUG) - -#include - -#define WATCHPOINT fprintf(stderr, "\nWATCHPOINT %s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout); -#define WATCHPOINT_ERROR(A) fprintf(stderr, "\nWATCHPOINT %s:%d %s\n", __FILE__, __LINE__, memcached_strerror(NULL, A));fflush(stdout); -#define WATCHPOINT_IFERROR(A) if(A != MEMCACHED_SUCCESS)fprintf(stderr, "\nWATCHPOINT %s:%d %s\n", __FILE__, __LINE__, memcached_strerror(NULL, A));fflush(stdout); -#define WATCHPOINT_STRING(A) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %s\n", __FILE__, __LINE__,__func__,A);fflush(stdout); -#define WATCHPOINT_STRING_LENGTH(A,B) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %.*s\n", __FILE__, __LINE__,__func__,(int)B,A);fflush(stdout); -#define WATCHPOINT_NUMBER(A) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %zu\n", __FILE__, __LINE__,__func__,(size_t)(A));fflush(stdout); -#define WATCHPOINT_ERRNO(A) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %s\n", __FILE__, __LINE__,__func__, strerror(A));fflush(stdout); -#define WATCHPOINT_ASSERT_PRINT(A,B,C) if(!(A)){fprintf(stderr, "\nWATCHPOINT ASSERT %s:%d (%s) ", __FILE__, __LINE__,__func__);fprintf(stderr, (B),(C));fprintf(stderr,"\n");fflush(stdout);}assert((A)); -#define WATCHPOINT_ASSERT(A) assert((A)); -#define WATCHPOINT_ASSERT_INITIALIZED(A) (memcached_is_initialized((A)); - -#else - -#define WATCHPOINT -#define WATCHPOINT_ERROR(A) -#define WATCHPOINT_IFERROR(A) -#define WATCHPOINT_STRING(A) -#define WATCHPOINT_NUMBER(A) -#define WATCHPOINT_ERRNO(A) -#define WATCHPOINT_ASSERT_PRINT(A,B,C) -#define WATCHPOINT_ASSERT(A) -#define WATCHPOINT_ASSERT_INITIALIZED(A) - -#endif /* DEBUG */ - -#endif /* LIBMEMCACHED_MEMCACHED_WATCHPOINT_H */ diff --git a/libmemcached/parse.c b/libmemcached/parse.c new file mode 100644 index 00000000..73cc9dfe --- /dev/null +++ b/libmemcached/parse.c @@ -0,0 +1,74 @@ +/* + I debated about putting this in the client library since it does an + action I don't really believe belongs in the library. + + Frankly its too damn useful not to be here though. +*/ + +#include "common.h" + +memcached_server_st *memcached_servers_parse(const char *server_strings) +{ + char *string; + uint32_t port; + uint32_t weight; + const char *begin_ptr; + const char *end_ptr; + memcached_server_st *servers= NULL; + memcached_return_t rc; + + WATCHPOINT_ASSERT(server_strings); + + end_ptr= server_strings + strlen(server_strings); + + for (begin_ptr= server_strings, string= index(server_strings, ','); + begin_ptr != end_ptr; + string= index(begin_ptr, ',')) + { + char buffer[HUGE_STRING_LEN]; + char *ptr, *ptr2; + port= 0; + weight= 0; + + if (string) + { + memcpy(buffer, begin_ptr, (size_t) (string - begin_ptr)); + buffer[(unsigned int)(string - begin_ptr)]= 0; + begin_ptr= string+1; + } + else + { + size_t length= strlen(begin_ptr); + memcpy(buffer, begin_ptr, length); + buffer[length]= 0; + begin_ptr= end_ptr; + } + + ptr= index(buffer, ':'); + + if (ptr) + { + ptr[0]= 0; + + ptr++; + + port= (uint32_t) strtoul(ptr, (char **)NULL, 10); + + ptr2= index(ptr, ' '); + if (! ptr2) + ptr2= index(ptr, ':'); + if (ptr2) + { + ptr2++; + weight = (uint32_t) strtoul(ptr2, (char **)NULL, 10); + } + } + + servers= memcached_server_list_append_with_weight(servers, buffer, port, weight, &rc); + + if (isspace(*begin_ptr)) + begin_ptr++; + } + + return servers; +} diff --git a/libmemcached/pool.h b/libmemcached/pool.h new file mode 100644 index 00000000..84dab3dd --- /dev/null +++ b/libmemcached/pool.h @@ -0,0 +1,51 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Connection pool implementation for libmemcached. + * + */ + + +#ifndef MEMCACHED_POOL_H +#define MEMCACHED_POOL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct memcached_pool_st; +typedef struct memcached_pool_st memcached_pool_st; + +LIBMEMCACHED_API +memcached_pool_st *memcached_pool_create(memcached_st* mmc, uint32_t initial, + uint32_t max); +LIBMEMCACHED_API +memcached_st* memcached_pool_destroy(memcached_pool_st* pool); +LIBMEMCACHED_API +memcached_st* memcached_pool_pop(memcached_pool_st* pool, + bool block, + memcached_return_t* rc); +LIBMEMCACHED_API +memcached_return_t memcached_pool_push(memcached_pool_st* pool, + memcached_st* mmc); + +LIBMEMCACHED_API +memcached_return_t memcached_pool_behavior_set(memcached_pool_st *ptr, + memcached_behavior_t flag, + uint64_t data); +LIBMEMCACHED_API +memcached_return_t memcached_pool_behavior_get(memcached_pool_st *ptr, + memcached_behavior_t flag, + uint64_t *value); + +#ifdef __cplusplus +} +#endif + +#endif /* MEMCACHED_POOL_H */ diff --git a/libmemcached/purge.c b/libmemcached/purge.c new file mode 100644 index 00000000..e3f857b4 --- /dev/null +++ b/libmemcached/purge.c @@ -0,0 +1,74 @@ +#include "common.h" + +memcached_return_t memcached_purge(memcached_server_st *ptr) +{ + uint32_t x; + memcached_return_t ret= MEMCACHED_SUCCESS; + + if (ptr->root->options.is_purging || /* already purging */ + (memcached_server_response_count(ptr) < ptr->root->io_msg_watermark && + ptr->io_bytes_sent < ptr->root->io_bytes_watermark) || + (ptr->io_bytes_sent >= ptr->root->io_bytes_watermark && + memcached_server_response_count(ptr) < 2)) + { + return MEMCACHED_SUCCESS; + } + + /* memcached_io_write and memcached_response may call memcached_purge + so we need to be able stop any recursion.. */ + ptr->root->options.is_purging= true; + + WATCHPOINT_ASSERT(ptr->fd != -1); + /* Force a flush of the buffer to ensure that we don't have the n-1 pending + requests buffered up.. */ + if (memcached_io_write(ptr, NULL, 0, 1) == -1) + { + ptr->root->options.is_purging= true; + return MEMCACHED_WRITE_FAILURE; + } + WATCHPOINT_ASSERT(ptr->fd != -1); + + uint32_t no_msg= memcached_server_response_count(ptr) - 1; + if (no_msg > 0) + { + memcached_result_st result; + memcached_result_st *result_ptr; + char buffer[SMALL_STRING_LEN]; + + /* + * We need to increase the timeout, because we might be waiting for + * data to be sent from the server (the commands was in the output buffer + * and just flushed + */ + int32_t timeo= ptr->root->poll_timeout; + ptr->root->poll_timeout= 2000; + + result_ptr= memcached_result_create(ptr->root, &result); + WATCHPOINT_ASSERT(result_ptr); + + for (x= 0; x < no_msg; x++) + { + memcached_result_reset(result_ptr); + memcached_return_t rc= memcached_read_one_response(ptr, buffer, + sizeof (buffer), + result_ptr); + /* + * Purge doesn't care for what kind of command results that is received. + * The only kind of errors I care about if is I'm out of sync with the + * protocol or have problems reading data from the network.. + */ + if (rc== MEMCACHED_PROTOCOL_ERROR || rc == MEMCACHED_UNKNOWN_READ_FAILURE) + { + WATCHPOINT_ERROR(rc); + ret = rc; + memcached_io_reset(ptr); + } + } + + memcached_result_free(result_ptr); + ptr->root->poll_timeout= timeo; + } + ptr->root->options.is_purging= false; + + return ret; +} diff --git a/libmemcached/quit.c b/libmemcached/quit.c new file mode 100644 index 00000000..3c117ade --- /dev/null +++ b/libmemcached/quit.c @@ -0,0 +1,77 @@ +#include "common.h" + +/* + This closes all connections (forces flush of input as well). + + Maybe add a host specific, or key specific version? + + The reason we send "quit" is that in case we have buffered IO, this + will force data to be completed. +*/ + +void memcached_quit_server(memcached_server_st *ptr, uint8_t io_death) +{ + if (ptr->fd != -1) + { + if (io_death == 0 && ptr->type != MEMCACHED_CONNECTION_UDP) + { + memcached_return_t rc; + char buffer[MEMCACHED_MAX_BUFFER]; + + if (ptr->root->flags.binary_protocol) + { + protocol_binary_request_quit request = {.bytes= {0}}; + request.message.header.request.magic = PROTOCOL_BINARY_REQ; + request.message.header.request.opcode = PROTOCOL_BINARY_CMD_QUIT; + request.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; + rc= memcached_do(ptr, request.bytes, sizeof(request.bytes), 1); + } + else + rc= memcached_do(ptr, "quit\r\n", 6, 1); + + WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_FETCH_NOTFINISHED); + + /* read until socket is closed, or there is an error + * closing the socket before all data is read + * results in server throwing away all data which is + * not read + */ + ssize_t nread; + while (memcached_io_read(ptr, buffer, sizeof(buffer)/sizeof(*buffer), + &nread) == MEMCACHED_SUCCESS); + + /* + * memcached_io_read may call memcached_quit_server with io_death if + * it encounters problems, but we don't care about those occurences. + * The intention of that loop is to drain the data sent from the + * server to ensure that the server processed all of the data we + * sent to the server. + */ + ptr->server_failure_counter= 0; + } + memcached_io_close(ptr); + + ptr->fd= -1; + ptr->write_buffer_offset= (size_t) ((ptr->type == MEMCACHED_CONNECTION_UDP) ? UDP_DATAGRAM_HEADER_LENGTH : 0); + ptr->read_buffer_length= 0; + ptr->read_ptr= ptr->read_buffer; + memcached_server_response_reset(ptr); + } + + if(io_death) ptr->server_failure_counter++; +} + +void memcached_quit(memcached_st *ptr) +{ + unsigned int x; + + if (ptr->hosts == NULL || + ptr->number_of_hosts == 0) + return; + + if (ptr->hosts && ptr->number_of_hosts) + { + for (x= 0; x < ptr->number_of_hosts; x++) + memcached_quit_server(&ptr->hosts[x], 0); + } +} diff --git a/libmemcached/response.c b/libmemcached/response.c new file mode 100644 index 00000000..a87ac249 --- /dev/null +++ b/libmemcached/response.c @@ -0,0 +1,519 @@ +/* + Memcached library + + memcached_response() is used to determine the return result + from an issued command. +*/ + +#include "common.h" + +static memcached_return_t textual_read_one_response(memcached_server_st *ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result); +static memcached_return_t binary_read_one_response(memcached_server_st *ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result); + +memcached_return_t memcached_read_one_response(memcached_server_st *ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + memcached_server_response_decrement(ptr); + + if (result == NULL) + result = &ptr->root->result; + + memcached_return_t rc; + if (ptr->root->flags.binary_protocol) + rc= binary_read_one_response(ptr, buffer, buffer_length, result); + else + rc= textual_read_one_response(ptr, buffer, buffer_length, result); + + unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE || + rc == MEMCACHED_PROTOCOL_ERROR || + rc == MEMCACHED_CLIENT_ERROR || + rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) + memcached_io_reset(ptr); + + return rc; +} + +memcached_return_t memcached_response(memcached_server_st *ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + /* We may have old commands in the buffer not set, first purge */ + if (ptr->root->flags.no_block) + (void)memcached_io_write(ptr, NULL, 0, 1); + + /* + * The previous implementation purged all pending requests and just + * returned the last one. Purge all pending messages to ensure backwards + * compatibility. + */ + if (ptr->root->flags.binary_protocol == false) + while (memcached_server_response_count(ptr) > 1) + { + memcached_return_t rc= memcached_read_one_response(ptr, buffer, buffer_length, result); + + unlikely (rc != MEMCACHED_END && + rc != MEMCACHED_STORED && + rc != MEMCACHED_SUCCESS && + rc != MEMCACHED_STAT && + rc != MEMCACHED_DELETED && + rc != MEMCACHED_NOTFOUND && + rc != MEMCACHED_NOTSTORED && + rc != MEMCACHED_DATA_EXISTS) + return rc; + } + + return memcached_read_one_response(ptr, buffer, buffer_length, result); +} + +static memcached_return_t textual_value_fetch(memcached_server_st *ptr, + char *buffer, + memcached_result_st *result) +{ + memcached_return_t rc= MEMCACHED_SUCCESS; + char *string_ptr; + char *end_ptr; + char *next_ptr; + size_t value_length; + size_t to_read; + char *value_ptr; + + if (ptr->root->flags.use_udp) + return MEMCACHED_NOT_SUPPORTED; + + WATCHPOINT_ASSERT(ptr->root); + end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE; + + memcached_result_reset(result); + + string_ptr= buffer; + string_ptr+= 6; /* "VALUE " */ + + + /* We load the key */ + { + char *key; + size_t prefix_length; + + key= result->key; + result->key_length= 0; + + for (prefix_length= ptr->root->prefix_key_length; !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++) + { + if (prefix_length == 0) + { + *key= *string_ptr; + key++; + result->key_length++; + } + else + prefix_length--; + } + result->key[result->key_length]= 0; + } + + if (end_ptr == string_ptr) + goto read_error; + + /* Flags fetch move past space */ + string_ptr++; + if (end_ptr == string_ptr) + goto read_error; + for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); + result->flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10); + + if (end_ptr == string_ptr) + goto read_error; + + /* Length fetch move past space*/ + string_ptr++; + if (end_ptr == string_ptr) + goto read_error; + + for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); + value_length= (size_t)strtoull(next_ptr, &string_ptr, 10); + + if (end_ptr == string_ptr) + goto read_error; + + /* Skip spaces */ + if (*string_ptr == '\r') + { + /* Skip past the \r\n */ + string_ptr+= 2; + } + else + { + string_ptr++; + for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++); + result->cas= strtoull(next_ptr, &string_ptr, 10); + } + + if (end_ptr < string_ptr) + goto read_error; + + /* We add two bytes so that we can walk the \r\n */ + rc= memcached_string_check(&result->value, value_length+2); + if (rc != MEMCACHED_SUCCESS) + { + value_length= 0; + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + } + + value_ptr= memcached_string_value(&result->value); + /* + We read the \r\n into the string since not doing so is more + cycles then the waster of memory to do so. + + We are null terminating through, which will most likely make + some people lazy about using the return length. + */ + to_read= (value_length) + 2; + ssize_t read_length= 0; + memcached_return_t rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length); + if (rrc != MEMCACHED_SUCCESS) + return rrc; + + if (read_length != (ssize_t)(value_length + 2)) + { + goto read_error; + } + +/* This next bit blows the API, but this is internal....*/ + { + char *char_ptr; + char_ptr= memcached_string_value(&result->value);; + char_ptr[value_length]= 0; + char_ptr[value_length + 1]= 0; + memcached_string_set_length(&result->value, value_length); + } + + return MEMCACHED_SUCCESS; + +read_error: + memcached_io_reset(ptr); + + return MEMCACHED_PARTIAL_READ; +} + +static memcached_return_t textual_read_one_response(memcached_server_st *ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length); + if (rc != MEMCACHED_SUCCESS) + return rc; + + switch(buffer[0]) + { + case 'V': /* VALUE || VERSION */ + if (buffer[1] == 'A') /* VALUE */ + { + /* We add back in one because we will need to search for END */ + memcached_server_response_increment(ptr); + return textual_value_fetch(ptr, buffer, result); + } + else if (buffer[1] == 'E') /* VERSION */ + { + return MEMCACHED_SUCCESS; + } + else + { + WATCHPOINT_STRING(buffer); + WATCHPOINT_ASSERT(0); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + case 'O': /* OK */ + return MEMCACHED_SUCCESS; + case 'S': /* STORED STATS SERVER_ERROR */ + { + if (buffer[2] == 'A') /* STORED STATS */ + { + memcached_server_response_increment(ptr); + return MEMCACHED_STAT; + } + else if (buffer[1] == 'E') /* SERVER_ERROR */ + { + char *rel_ptr; + char *startptr= buffer + 13, *endptr= startptr; + + while (*endptr != '\r' && *endptr != '\n') endptr++; + + /* + Yes, we could make this "efficent" but to do that we would need + to maintain more state for the size of the buffer. Why waste + memory in the struct, which is important, for something that + rarely should happen? + */ + rel_ptr= (char *)ptr->root->call_realloc(ptr->root, + ptr->cached_server_error, + (size_t) (endptr - startptr + 1)); + + if (rel_ptr == NULL) + { + /* If we happened to have some memory, we just null it since we don't know the size */ + if (ptr->cached_server_error) + ptr->cached_server_error[0]= 0; + return MEMCACHED_SERVER_ERROR; + } + ptr->cached_server_error= rel_ptr; + + memcpy(ptr->cached_server_error, startptr, (size_t) (endptr - startptr)); + ptr->cached_server_error[endptr - startptr]= 0; + return MEMCACHED_SERVER_ERROR; + } + else if (buffer[1] == 'T') + return MEMCACHED_STORED; + else + { + WATCHPOINT_STRING(buffer); + WATCHPOINT_ASSERT(0); + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + case 'D': /* DELETED */ + return MEMCACHED_DELETED; + case 'N': /* NOT_FOUND */ + { + if (buffer[4] == 'F') + return MEMCACHED_NOTFOUND; + else if (buffer[4] == 'S') + return MEMCACHED_NOTSTORED; + else + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + case 'E': /* PROTOCOL ERROR or END */ + { + if (buffer[1] == 'N') + return MEMCACHED_END; + else if (buffer[1] == 'R') + return MEMCACHED_PROTOCOL_ERROR; + else if (buffer[1] == 'X') + return MEMCACHED_DATA_EXISTS; + else + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + case 'I': /* CLIENT ERROR */ + /* We add back in one because we will need to search for END */ + memcached_server_response_increment(ptr); + return MEMCACHED_ITEM; + case 'C': /* CLIENT ERROR */ + return MEMCACHED_CLIENT_ERROR; + default: + { + unsigned long long auto_return_value; + + if (sscanf(buffer, "%llu", &auto_return_value) == 1) + return MEMCACHED_SUCCESS; + + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + + /* NOTREACHED */ +} + +char *memcached_result_value(memcached_result_st *ptr) +{ + memcached_string_st *sptr= &ptr->value; + return memcached_string_value(sptr); +} + +size_t memcached_result_length(memcached_result_st *ptr) +{ + memcached_string_st *sptr= &ptr->value; + return memcached_string_length(sptr); +} + +static memcached_return_t binary_read_one_response(memcached_server_st *ptr, + char *buffer, size_t buffer_length, + memcached_result_st *result) +{ + protocol_binary_response_header header; + + unlikely (memcached_safe_read(ptr, &header.bytes, + sizeof(header.bytes)) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + + unlikely (header.response.magic != PROTOCOL_BINARY_RES) + return MEMCACHED_PROTOCOL_ERROR; + + /* + ** Convert the header to host local endian! + */ + header.response.keylen= ntohs(header.response.keylen); + header.response.status= ntohs(header.response.status); + header.response.bodylen= ntohl(header.response.bodylen); + header.response.cas= ntohll(header.response.cas); + uint32_t bodylen= header.response.bodylen; + + if (header.response.status == 0) + { + switch (header.response.opcode) + { + case PROTOCOL_BINARY_CMD_GETKQ: + /* + * We didn't increment the response counter for the GETKQ packet + * (only the final NOOP), so we need to increment the counter again. + */ + memcached_server_response_increment(ptr); + /* FALLTHROUGH */ + case PROTOCOL_BINARY_CMD_GETK: + { + uint16_t keylen= header.response.keylen; + memcached_result_reset(result); + result->cas= header.response.cas; + + if (memcached_safe_read(ptr, &result->flags, + sizeof (result->flags)) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + + result->flags= ntohl(result->flags); + bodylen -= header.response.extlen; + + result->key_length= keylen; + if (memcached_safe_read(ptr, result->key, keylen) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + + bodylen -= keylen; + if (memcached_string_check(&result->value, + bodylen) != MEMCACHED_SUCCESS) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + char *vptr= memcached_string_value(&result->value); + if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + + memcached_string_set_length(&result->value, bodylen); + } + break; + case PROTOCOL_BINARY_CMD_INCREMENT: + case PROTOCOL_BINARY_CMD_DECREMENT: + { + if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t)) + return MEMCACHED_PROTOCOL_ERROR; + + WATCHPOINT_ASSERT(bodylen == buffer_length); + uint64_t val; + if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + + val= ntohll(val); + memcpy(buffer, &val, sizeof(val)); + } + break; + case PROTOCOL_BINARY_CMD_VERSION: + { + memset(buffer, 0, buffer_length); + if (bodylen >= buffer_length) + /* not enough space in buffer.. should not happen... */ + return MEMCACHED_UNKNOWN_READ_FAILURE; + else if (memcached_safe_read(ptr, buffer, bodylen) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + break; + case PROTOCOL_BINARY_CMD_FLUSH: + case PROTOCOL_BINARY_CMD_QUIT: + case PROTOCOL_BINARY_CMD_SET: + case PROTOCOL_BINARY_CMD_ADD: + case PROTOCOL_BINARY_CMD_REPLACE: + case PROTOCOL_BINARY_CMD_APPEND: + case PROTOCOL_BINARY_CMD_PREPEND: + case PROTOCOL_BINARY_CMD_DELETE: + { + WATCHPOINT_ASSERT(bodylen == 0); + return MEMCACHED_SUCCESS; + } + case PROTOCOL_BINARY_CMD_NOOP: + { + WATCHPOINT_ASSERT(bodylen == 0); + return MEMCACHED_END; + } + case PROTOCOL_BINARY_CMD_STAT: + { + if (bodylen == 0) + return MEMCACHED_END; + else if (bodylen + 1 > buffer_length) + /* not enough space in buffer.. should not happen... */ + return MEMCACHED_UNKNOWN_READ_FAILURE; + else + { + size_t keylen= header.response.keylen; + memset(buffer, 0, buffer_length); + if (memcached_safe_read(ptr, buffer, keylen) != MEMCACHED_SUCCESS || + memcached_safe_read(ptr, buffer + keylen + 1, + bodylen - keylen) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + } + } + break; + default: + { + /* Command not implemented yet! */ + WATCHPOINT_ASSERT(0); + return MEMCACHED_PROTOCOL_ERROR; + } + } + } + else if (header.response.bodylen) + { + /* What should I do with the error message??? just discard it for now */ + char hole[SMALL_STRING_LEN]; + while (bodylen > 0) + { + size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen; + if (memcached_safe_read(ptr, hole, nr) != MEMCACHED_SUCCESS) + return MEMCACHED_UNKNOWN_READ_FAILURE; + bodylen-= (uint32_t) nr; + } + + /* This might be an error from one of the quiet commands.. if + * so, just throw it away and get the next one. What about creating + * a callback to the user with the error information? + */ + switch (header.response.opcode) + { + case PROTOCOL_BINARY_CMD_SETQ: + case PROTOCOL_BINARY_CMD_ADDQ: + case PROTOCOL_BINARY_CMD_REPLACEQ: + case PROTOCOL_BINARY_CMD_APPENDQ: + case PROTOCOL_BINARY_CMD_PREPENDQ: + return binary_read_one_response(ptr, buffer, buffer_length, result); + default: + break; + } + } + + memcached_return_t rc= MEMCACHED_SUCCESS; + unlikely(header.response.status != 0) + switch (header.response.status) + { + case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT: + rc= MEMCACHED_NOTFOUND; + break; + case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS: + rc= MEMCACHED_DATA_EXISTS; + break; + case PROTOCOL_BINARY_RESPONSE_NOT_STORED: + rc= MEMCACHED_NOTSTORED; + break; + case PROTOCOL_BINARY_RESPONSE_E2BIG: + rc= MEMCACHED_E2BIG; + break; + case PROTOCOL_BINARY_RESPONSE_ENOMEM: + rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + break; + case PROTOCOL_BINARY_RESPONSE_EINVAL: + case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: + default: + /* @todo fix the error mappings */ + rc= MEMCACHED_PROTOCOL_ERROR; + break; + } + + return rc; +} diff --git a/libmemcached/result.c b/libmemcached/result.c new file mode 100644 index 00000000..14a0774e --- /dev/null +++ b/libmemcached/result.c @@ -0,0 +1,81 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Functions to manipulate the result structure. + * + */ + +/* + memcached_result_st are used to internally represent the return values from + memcached. We use a structure so that long term as identifiers are added + to memcached we will be able to absorb new attributes without having + to addjust the entire API. +*/ +#include "common.h" + +memcached_result_st *memcached_result_create(memcached_st *memc, + memcached_result_st *ptr) +{ + WATCHPOINT_ASSERT(memc && memc->options.is_initialized); + + /* Saving malloc calls :) */ + if (ptr) + { + memset(ptr, 0, sizeof(memcached_result_st)); + } + else + { + ptr= memc->call_malloc(memc, sizeof(memcached_result_st)); + + if (ptr == NULL) + return NULL; + ptr->options.is_allocated= true; + } + + ptr->options.is_initialized= true; + + ptr->root= memc; + memcached_string_create(memc, &ptr->value, 0); + WATCHPOINT_ASSERT_INITIALIZED(&ptr->value); + WATCHPOINT_ASSERT(ptr->value.string == NULL); + + return ptr; +} + +void memcached_result_reset(memcached_result_st *ptr) +{ + ptr->key_length= 0; + memcached_string_reset(&ptr->value); + ptr->flags= 0; + ptr->cas= 0; + ptr->expiration= 0; +} + +/* + NOTE turn into macro +*/ +memcached_return_t memcached_result_set_value(memcached_result_st *ptr, const char *value, size_t length) +{ + return memcached_string_append(&ptr->value, value, length); +} + +void memcached_result_free(memcached_result_st *ptr) +{ + if (ptr == NULL) + return; + + memcached_string_free(&ptr->value); + + if (memcached_is_allocated(ptr)) + { + free(ptr); + } + else + { + ptr->options.is_initialized= false; + } +} diff --git a/libmemcached/result.h b/libmemcached/result.h new file mode 100644 index 00000000..4b0c12e5 --- /dev/null +++ b/libmemcached/result.h @@ -0,0 +1,65 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Functions to manipulate the result structure. + * + */ + +#ifndef __MEMCACHED_RESULT_H__ +#define __MEMCACHED_RESULT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct memcached_result_st { + struct { + bool is_allocated:1; + bool is_initialized:1; + } options; + uint32_t flags; + time_t expiration; + memcached_st *root; + size_t key_length; + uint64_t cas; + memcached_string_st value; + char key[MEMCACHED_MAX_KEY]; + /* Add result callback function */ +}; + +/* Result Struct */ +LIBMEMCACHED_API +void memcached_result_free(memcached_result_st *result); +LIBMEMCACHED_API +void memcached_result_reset(memcached_result_st *ptr); +LIBMEMCACHED_API +memcached_result_st *memcached_result_create(memcached_st *ptr, + memcached_result_st *result); +#define memcached_result_key_value(A) (A)->key +#define memcached_result_key_length(A) (A)->key_length +#define memcached_result_string_st(A) ((A)->value) +#ifdef FIX +#define memcached_result_value(A) memcached_string_value((A)->value) +#define memcached_result_length(A) memcached_string_length((A)->value) +#else +LIBMEMCACHED_API +char *memcached_result_value(memcached_result_st *ptr); +LIBMEMCACHED_API +size_t memcached_result_length(memcached_result_st *ptr); +#endif +#define memcached_result_flags(A) (A)->flags +#define memcached_result_cas(A) (A)->cas +LIBMEMCACHED_API +memcached_return_t memcached_result_set_value(memcached_result_st *ptr, const char *value, size_t length); +#define memcached_result_set_flags(A,B) (A)->flags=(B) +#define memcached_result_set_expiration(A,B) (A)->expiration=(B) + +#ifdef __cplusplus +} +#endif + +#endif /* __MEMCACHED_RESULT_H__ */ diff --git a/libmemcached/server.c b/libmemcached/server.c new file mode 100644 index 00000000..2cff36da --- /dev/null +++ b/libmemcached/server.c @@ -0,0 +1,179 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: String structure used for libmemcached. + * + */ + +/* + This is a partial implementation for fetching/creating memcached_server_st objects. +*/ +#include "common.h" + +memcached_server_st *memcached_server_create(memcached_st *memc, memcached_server_st *ptr) +{ + if (ptr == NULL) + { + ptr= (memcached_server_st *)calloc(1, sizeof(memcached_server_st)); + + if (!ptr) + return NULL; /* MEMCACHED_MEMORY_ALLOCATION_FAILURE */ + + ptr->options.is_allocated= true; + } + else + { + memset(ptr, 0, sizeof(memcached_server_st)); + } + + ptr->root= memc; + + return ptr; +} + +memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host, + const char *hostname, in_port_t port, + uint32_t weight, memcached_connection_t type) +{ + host= memcached_server_create(memc, host); + + if (host == NULL) + return NULL; + + strncpy(host->hostname, hostname, MEMCACHED_MAX_HOST_LENGTH - 1); + host->root= memc ? memc : NULL; + host->port= port; + host->weight= weight; + host->fd= -1; + host->type= type; + host->read_ptr= host->read_buffer; + if (memc) + host->next_retry= memc->retry_timeout; + if (type == MEMCACHED_CONNECTION_UDP) + { + host->write_buffer_offset= UDP_DATAGRAM_HEADER_LENGTH; + memcached_io_init_udp_header(host, 0); + } + + return host; +} + +void memcached_server_free(memcached_server_st *ptr) +{ + memcached_quit_server(ptr, 0); + + if (ptr->cached_server_error) + free(ptr->cached_server_error); + + if (ptr->address_info) + freeaddrinfo(ptr->address_info); + + + if (memcached_is_allocated(ptr)) + { + ptr->root->call_free(ptr->root, ptr); + } + else + { + memset(ptr, 0, sizeof(memcached_server_st)); + } +} + +/* + If we do not have a valid object to clone from, we toss an error. +*/ +memcached_server_st *memcached_server_clone(memcached_server_st *clone, memcached_server_st *ptr) +{ + memcached_server_st *rv= NULL; + + /* We just do a normal create if ptr is missing */ + if (ptr == NULL) + return NULL; + + rv = memcached_server_create_with(ptr->root, clone, + ptr->hostname, ptr->port, ptr->weight, + ptr->type); + if (rv != NULL) + { + rv->cached_errno= ptr->cached_errno; + if (ptr->cached_server_error) + rv->cached_server_error= strdup(ptr->cached_server_error); + } + + return rv; + +} + +memcached_return_t memcached_server_cursor(memcached_st *ptr, + memcached_server_fn *callback, + void *context, + uint32_t number_of_callbacks) +{ + unsigned int y; + + for (y= 0; y < ptr->number_of_hosts; y++) + { + unsigned int x; + + for (x= 0; x < number_of_callbacks; x++) + { + unsigned int iferror; + + iferror= (*callback[x])(ptr, &ptr->hosts[y], context); + + if (iferror) + continue; + } + } + + return MEMCACHED_SUCCESS; +} + +memcached_server_st *memcached_server_by_key(memcached_st *ptr, const char *key, size_t key_length, memcached_return_t *error) +{ + uint32_t server_key; + + *error= memcached_validate_key_length(key_length, + ptr->flags.binary_protocol); + unlikely (*error != MEMCACHED_SUCCESS) + return NULL; + + unlikely (ptr->number_of_hosts == 0) + { + *error= MEMCACHED_NO_SERVERS; + return NULL; + } + + if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + { + *error= MEMCACHED_BAD_KEY_PROVIDED; + return NULL; + } + + server_key= memcached_generate_hash(ptr, key, key_length); + + return memcached_server_clone(NULL, &ptr->hosts[server_key]); + +} + +const char *memcached_server_error(memcached_server_st *ptr) +{ + if (ptr) + return ptr->cached_server_error; + else + return NULL; +} + +void memcached_server_error_reset(memcached_server_st *ptr) +{ + ptr->cached_server_error[0]= 0; +} + +memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr) +{ + return ptr->last_disconnected_server; +} diff --git a/libmemcached/server.h b/libmemcached/server.h new file mode 100644 index 00000000..f2d92dc1 --- /dev/null +++ b/libmemcached/server.h @@ -0,0 +1,101 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: String structure used for libmemcached. + * + */ + +#ifndef __MEMCACHED_SERVER_H__ +#define __MEMCACHED_SERVER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct memcached_server_st { + struct { + bool is_allocated:1; + } options; + bool sockaddr_inited; + uint16_t count; + uint32_t cursor_active; + in_port_t port; + int cached_errno; + int fd; + uint32_t io_bytes_sent; /* # bytes sent since last read */ + uint32_t server_failure_counter; + uint32_t weight; + uint8_t major_version; + uint8_t micro_version; + uint8_t minor_version; + memcached_connection_t type; + char *read_ptr; + char *cached_server_error; + size_t read_buffer_length; + size_t read_data_length; + size_t write_buffer_offset; + struct addrinfo *address_info; + time_t next_retry; + memcached_st *root; + uint64_t limit_maxbytes; + char read_buffer[MEMCACHED_MAX_BUFFER]; + char write_buffer[MEMCACHED_MAX_BUFFER]; + char hostname[MEMCACHED_MAX_HOST_LENGTH]; +}; + +#define memcached_server_count(A) (A)->number_of_hosts +#define memcached_server_name(A,B) (B).hostname +#define memcached_server_port(A,B) (B).port +#define memcached_server_list(A) (A)->hosts +#define memcached_server_response_count(A) (A)->cursor_active + +LIBMEMCACHED_API +memcached_return_t memcached_server_cursor(memcached_st *ptr, + memcached_server_fn *callback, + void *context, + uint32_t number_of_callbacks); + +LIBMEMCACHED_API +memcached_server_st *memcached_server_by_key(memcached_st *ptr, const char *key, + size_t key_length, memcached_return_t *error); + +LIBMEMCACHED_API +const char *memcached_server_error(memcached_server_st *ptr); + +LIBMEMCACHED_API +void memcached_server_error_reset(memcached_server_st *ptr); + +/* These should not currently be used by end users */ +/* TODO: Is the above comment valid? If so, how can we unit test these if they + * aren't exported. If not, we should remove the comment */ +LIBMEMCACHED_API +memcached_server_st *memcached_server_create(memcached_st *memc, memcached_server_st *ptr); + +LIBMEMCACHED_API +memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host, + const char *hostname, in_port_t port, + uint32_t weight, memcached_connection_t type); + +LIBMEMCACHED_API +void memcached_server_free(memcached_server_st *ptr); +LIBMEMCACHED_API +memcached_server_st *memcached_server_clone(memcached_server_st *clone, memcached_server_st *ptr); +LIBMEMCACHED_API +memcached_analysis_st *memcached_analyze(memcached_st *memc, memcached_stat_st *memc_stat, + memcached_return_t *error); + +LIBMEMCACHED_API +memcached_return_t memcached_server_remove(memcached_server_st *st_ptr); + +LIBMEMCACHED_API +memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* __MEMCACHED_SERVER_H__ */ diff --git a/libmemcached/stats.c b/libmemcached/stats.c new file mode 100644 index 00000000..e5f9a20f --- /dev/null +++ b/libmemcached/stats.c @@ -0,0 +1,456 @@ +/* +*/ + +#include "common.h" + +static const char *memcached_stat_keys[] = { + "pid", + "uptime", + "time", + "version", + "pointer_size", + "rusage_user", + "rusage_system", + "curr_items", + "total_items", + "bytes", + "curr_connections", + "total_connections", + "connection_structures", + "cmd_get", + "cmd_set", + "get_hits", + "get_misses", + "evictions", + "bytes_read", + "bytes_written", + "limit_maxbytes", + "threads", + NULL +}; + + +static memcached_return_t set_data(memcached_stat_st *memc_stat, char *key, char *value) +{ + + if (strlen(key) < 1) + { + WATCHPOINT_STRING(key); + return MEMCACHED_UNKNOWN_STAT_KEY; + } + else if (!strcmp("pid", key)) + { + memc_stat->pid= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("uptime", key)) + { + memc_stat->uptime= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("time", key)) + { + memc_stat->time= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("version", key)) + { + memcpy(memc_stat->version, value, strlen(value)); + memc_stat->version[strlen(value)]= 0; + } + else if (!strcmp("pointer_size", key)) + { + memc_stat->pointer_size= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("rusage_user", key)) + { + char *walk_ptr; + for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); + *walk_ptr= 0; + walk_ptr++; + memc_stat->rusage_user_seconds= (uint32_t) strtol(value, (char **)NULL, 10); + memc_stat->rusage_user_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); + } + else if (!strcmp("rusage_system", key)) + { + char *walk_ptr; + for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++); + *walk_ptr= 0; + walk_ptr++; + memc_stat->rusage_system_seconds= (uint32_t) strtol(value, (char **)NULL, 10); + memc_stat->rusage_system_microseconds= (uint32_t) strtol(walk_ptr, (char **)NULL, 10); + } + else if (!strcmp("curr_items", key)) + { + memc_stat->curr_items= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("total_items", key)) + { + memc_stat->total_items= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("bytes_read", key)) + { + memc_stat->bytes_read= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("bytes_written", key)) + { + memc_stat->bytes_written= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("bytes", key)) + { + memc_stat->bytes= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("curr_connections", key)) + { + memc_stat->curr_connections= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("total_connections", key)) + { + memc_stat->total_connections= (uint32_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("connection_structures", key)) + { + memc_stat->connection_structures= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!strcmp("cmd_get", key)) + { + memc_stat->cmd_get= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("cmd_set", key)) + { + memc_stat->cmd_set= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("get_hits", key)) + { + memc_stat->get_hits= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("get_misses", key)) + { + memc_stat->get_misses= (uint64_t)strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("evictions", key)) + { + memc_stat->evictions= (uint64_t)strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("limit_maxbytes", key)) + { + memc_stat->limit_maxbytes= (uint64_t) strtoll(value, (char **)NULL, 10); + } + else if (!strcmp("threads", key)) + { + memc_stat->threads= (uint32_t) strtol(value, (char **)NULL, 10); + } + else if (!(strcmp("delete_misses", key) == 0 ||/* New stats in the 1.3 beta */ + strcmp("delete_hits", key) == 0 ||/* Just swallow them for now.. */ + strcmp("incr_misses", key) == 0 || + strcmp("incr_hits", key) == 0 || + strcmp("decr_misses", key) == 0 || + strcmp("decr_hits", key) == 0 || + strcmp("cas_misses", key) == 0 || + strcmp("cas_hits", key) == 0 || + strcmp("cas_badval", key) == 0 || + strcmp("cmd_flush", key) == 0 || + strcmp("accepting_conns", key) == 0 || + strcmp("listen_disabled_num", key) == 0 || + strcmp("conn_yields", key) == 0)) + { + WATCHPOINT_STRING(key); + return MEMCACHED_UNKNOWN_STAT_KEY; + } + + return MEMCACHED_SUCCESS; +} + +char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat, + const char *key, memcached_return_t *error) +{ + char buffer[SMALL_STRING_LEN]; + int length; + char *ret; + + *error= MEMCACHED_SUCCESS; + + if (!memcmp("pid", key, strlen("pid"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pid); + else if (!memcmp("uptime", key, strlen("uptime"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->uptime); + else if (!memcmp("time", key, strlen("time"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->time); + else if (!memcmp("version", key, strlen("version"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%s", memc_stat->version); + else if (!memcmp("pointer_size", key, strlen("pointer_size"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->pointer_size); + else if (!memcmp("rusage_user", key, strlen("rusage_user"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_user_seconds, memc_stat->rusage_user_microseconds); + else if (!memcmp("rusage_system", key, strlen("rusage_system"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u.%u", memc_stat->rusage_system_seconds, memc_stat->rusage_system_microseconds); + else if (!memcmp("curr_items", key, strlen("curr_items"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_items); + else if (!memcmp("total_items", key, strlen("total_items"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_items); + else if (!memcmp("curr_connections", key, strlen("curr_connections"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_connections); + else if (!memcmp("total_connections", key, strlen("total_connections"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_connections); + else if (!memcmp("connection_structures", key, strlen("connection_structures"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->connection_structures); + else if (!memcmp("cmd_get", key, strlen("cmd_get"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_get); + else if (!memcmp("cmd_set", key, strlen("cmd_set"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_set); + else if (!memcmp("get_hits", key, strlen("get_hits"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_hits); + else if (!memcmp("get_misses", key, strlen("get_misses"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_misses); + else if (!memcmp("evictions", key, strlen("evictions"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->evictions); + else if (!memcmp("bytes_read", key, strlen("bytes_read"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read); + else if (!memcmp("bytes_written", key, strlen("bytes_written"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written); + else if (!memcmp("bytes", key, strlen("bytes"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes); + else if (!memcmp("limit_maxbytes", key, strlen("limit_maxbytes"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes); + else if (!memcmp("threads", key, strlen("threads"))) + length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->threads); + else + { + *error= MEMCACHED_NOTFOUND; + return NULL; + } + + ret= ptr->call_malloc(ptr, (size_t) (length + 1)); + memcpy(ret, buffer, (size_t) length); + ret[length]= '\0'; + + return ret; +} + +static memcached_return_t binary_stats_fetch(memcached_st *ptr, + memcached_stat_st *memc_stat, + char *args, + unsigned int server_key) +{ + memcached_return_t rc; + + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + protocol_binary_request_stats request= {.bytes= {0}}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + + if (args != NULL) + { + size_t len= strlen(args); + + rc= memcached_validate_key_length(len, true); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + request.message.header.request.keylen= htons((uint16_t)len); + request.message.header.request.bodylen= htonl((uint32_t) len); + + if ((memcached_do(&ptr->hosts[server_key], request.bytes, + sizeof(request.bytes), 0) != MEMCACHED_SUCCESS) || + (memcached_io_write(&ptr->hosts[server_key], args, len, 1) == -1)) + { + memcached_io_reset(&ptr->hosts[server_key]); + return MEMCACHED_WRITE_FAILURE; + } + } + else + { + if (memcached_do(&ptr->hosts[server_key], request.bytes, + sizeof(request.bytes), 1) != MEMCACHED_SUCCESS) + { + memcached_io_reset(&ptr->hosts[server_key]); + return MEMCACHED_WRITE_FAILURE; + } + } + + memcached_server_response_decrement(&ptr->hosts[server_key]); + do + { + rc= memcached_response(&ptr->hosts[server_key], buffer, + sizeof(buffer), NULL); + if (rc == MEMCACHED_END) + break; + + unlikely (rc != MEMCACHED_SUCCESS) + { + memcached_io_reset(&ptr->hosts[server_key]); + return rc; + } + + unlikely((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY) + { + WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); + WATCHPOINT_ASSERT(0); + } + } while (1); + + /* shit... memcached_response will decrement the counter, so I need to + ** reset it.. todo: look at this and try to find a better solution. + */ + ptr->hosts[server_key].cursor_active= 0; + + return MEMCACHED_SUCCESS; +} + +static memcached_return_t ascii_stats_fetch(memcached_st *ptr, + memcached_stat_st *memc_stat, + char *args, + unsigned int server_key) +{ + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + size_t send_length; + + if (args) + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "stats %s\r\n", args); + else + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "stats\r\n"); + + if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) + return MEMCACHED_WRITE_FAILURE; + + rc= memcached_do(&ptr->hosts[server_key], buffer, send_length, 1); + if (rc != MEMCACHED_SUCCESS) + goto error; + + while (1) + { + rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + if (rc == MEMCACHED_STAT) + { + char *string_ptr, *end_ptr; + char *key, *value; + + string_ptr= buffer; + string_ptr+= 5; /* Move past STAT */ + for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++); + key= string_ptr; + key[(size_t)(end_ptr-string_ptr)]= 0; + + string_ptr= end_ptr + 1; + for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++); + value= string_ptr; + value[(size_t)(end_ptr-string_ptr)]= 0; + string_ptr= end_ptr + 2; + unlikely((set_data(memc_stat, key, value)) == MEMCACHED_UNKNOWN_STAT_KEY) + { + WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY); + WATCHPOINT_ASSERT(0); + } + } + else + break; + } + +error: + if (rc == MEMCACHED_END) + return MEMCACHED_SUCCESS; + else + return rc; +} + +memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_return_t *error) +{ + unsigned int x; + memcached_return_t rc; + memcached_stat_st *stats; + + unlikely (ptr->flags.use_udp) + { + *error= MEMCACHED_NOT_SUPPORTED; + return NULL; + } + + stats= ptr->call_calloc(ptr, ptr->number_of_hosts, sizeof(memcached_stat_st)); + + if (!stats) + { + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + + rc= MEMCACHED_SUCCESS; + for (x= 0; x < ptr->number_of_hosts; x++) + { + memcached_return_t temp_return; + + if (ptr->flags.binary_protocol) + temp_return= binary_stats_fetch(ptr, stats + x, args, x); + else + temp_return= ascii_stats_fetch(ptr, stats + x, args, x); + + if (temp_return != MEMCACHED_SUCCESS) + rc= MEMCACHED_SOME_ERRORS; + } + + *error= rc; + return stats; +} + +memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args, + const char *hostname, in_port_t port) +{ + memcached_return_t rc; + memcached_st memc; + memcached_st *memc_ptr; + + memc_ptr= memcached_create(&memc); + WATCHPOINT_ASSERT(memc_ptr); + + memcached_server_add(&memc, hostname, port); + + if (memc.flags.binary_protocol) + rc= binary_stats_fetch(&memc, memc_stat, args, 0); + else + rc= ascii_stats_fetch(&memc, memc_stat, args, 0); + + memcached_free(&memc); + + return rc; +} + +/* + We make a copy of the keys since at some point in the not so distant future + we will add support for "found" keys. +*/ +char ** memcached_stat_get_keys(memcached_st *ptr, memcached_stat_st *memc_stat, + memcached_return_t *error) +{ + (void) memc_stat; + char **list; + size_t length= sizeof(memcached_stat_keys); + + list= ptr->call_malloc(ptr, length); + + if (!list) + { + *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE; + return NULL; + } + + memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys)); + + *error= MEMCACHED_SUCCESS; + + return list; +} + +void memcached_stat_free(memcached_st *ptr, memcached_stat_st *memc_stat) +{ + if (memc_stat == NULL) + { + WATCHPOINT_ASSERT(0); /* Be polite, but when debugging catch this as an error */ + return; + } + + if (ptr) + ptr->call_free(ptr, memc_stat); + else + free(memc_stat); +} diff --git a/libmemcached/storage.c b/libmemcached/storage.c new file mode 100644 index 00000000..5aa1d771 --- /dev/null +++ b/libmemcached/storage.c @@ -0,0 +1,519 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Storage related functions, aka set, replace,.. + * + */ + +#include "common.h" + +typedef enum { + SET_OP, + REPLACE_OP, + ADD_OP, + PREPEND_OP, + APPEND_OP, + CAS_OP, +} memcached_storage_action_t; + +/* Inline this */ +static inline const char *storage_op_string(memcached_storage_action_t verb) +{ + switch (verb) + { + case SET_OP: + return "set "; + case REPLACE_OP: + return "replace "; + case ADD_OP: + return "add "; + case PREPEND_OP: + return "prepend "; + case APPEND_OP: + return "append "; + case CAS_OP: + return "cas "; + default: + return "tosserror"; /* This is impossible, fixes issue for compiler warning in VisualStudio */ + } + + /* NOTREACHED */ +} + +static memcached_return_t memcached_send_binary(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char *key, + size_t key_length, + const char *value, + size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas, + memcached_storage_action_t verb); + +static inline memcached_return_t memcached_send(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas, + memcached_storage_action_t verb) +{ + char to_write; + size_t write_length; + ssize_t sent_length; + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + unsigned int server_key; + + WATCHPOINT_ASSERT(!(value == NULL && value_length > 0)); + + rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol); + unlikely (rc != MEMCACHED_SUCCESS) + return rc; + + unlikely (ptr->number_of_hosts == 0) + return MEMCACHED_NO_SERVERS; + + if (ptr->flags.verify_key && (memcached_key_test((const char **)&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED)) + return MEMCACHED_BAD_KEY_PROVIDED; + + if (ptr->flags.binary_protocol) + { + return memcached_send_binary(ptr, master_key, master_key_length, + key, key_length, + value, value_length, expiration, + flags, cas, verb); + } + + server_key= memcached_generate_hash(ptr, master_key, master_key_length); + + if (cas) + { + write_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%s %s%.*s %u %llu %zu %llu%s\r\n", + storage_op_string(verb), + ptr->prefix_key, + (int)key_length, key, flags, + (unsigned long long)expiration, value_length, + (unsigned long long)cas, + (ptr->flags.no_reply) ? " noreply" : ""); + } + else + { + char *buffer_ptr= buffer; + const char *command= storage_op_string(verb); + + /* Copy in the command, no space needed, we handle that in the command function*/ + memcpy(buffer_ptr, command, strlen(command)); + + /* Copy in the key prefix, switch to the buffer_ptr */ + buffer_ptr= memcpy(buffer_ptr + strlen(command) , ptr->prefix_key, strlen(ptr->prefix_key)); + + /* Copy in the key, adjust point if a key prefix was used. */ + buffer_ptr= memcpy(buffer_ptr + (ptr->prefix_key ? strlen(ptr->prefix_key) : 0), + key, key_length); + buffer_ptr+= key_length; + buffer_ptr[0]= ' '; + buffer_ptr++; + + write_length= (size_t)(buffer_ptr - buffer); + write_length+= (size_t) snprintf(buffer_ptr, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%u %llu %zu%s\r\n", + flags, + (unsigned long long)expiration, value_length, + ptr->flags.no_reply ? " noreply" : ""); + } + + if (ptr->flags.use_udp && ptr->flags.buffer_requests) + { + size_t cmd_size= write_length + value_length + 2; + if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + return MEMCACHED_WRITE_FAILURE; + if (cmd_size + ptr->hosts[server_key].write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + memcached_io_write(&ptr->hosts[server_key], NULL, 0, 1); + } + + if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) + { + rc= MEMCACHED_WRITE_FAILURE; + goto error; + } + + /* Send command header */ + rc= memcached_do(&ptr->hosts[server_key], buffer, write_length, 0); + if (rc != MEMCACHED_SUCCESS) + goto error; + + /* Send command body */ + if ((sent_length= memcached_io_write(&ptr->hosts[server_key], value, value_length, 0)) == -1) + { + rc= MEMCACHED_WRITE_FAILURE; + goto error; + } + + if (ptr->flags.buffer_requests && verb == SET_OP) + { + to_write= 0; + } + else + { + to_write= 1; + } + + if ((sent_length= memcached_io_write(&ptr->hosts[server_key], "\r\n", 2, to_write)) == -1) + { + rc= MEMCACHED_WRITE_FAILURE; + goto error; + } + + if (ptr->flags.no_reply) + return (to_write == 0) ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS; + + if (to_write == 0) + return MEMCACHED_BUFFERED; + + rc= memcached_response(&ptr->hosts[server_key], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + if (rc == MEMCACHED_STORED) + return MEMCACHED_SUCCESS; + else + return rc; + +error: + memcached_io_reset(&ptr->hosts[server_key]); + + return rc; +} + + +memcached_return_t memcached_set(memcached_st *ptr, const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_SET_START(); + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, SET_OP); + LIBMEMCACHED_MEMCACHED_SET_END(); + return rc; +} + +memcached_return_t memcached_add(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_ADD_START(); + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, ADD_OP); + LIBMEMCACHED_MEMCACHED_ADD_END(); + return rc; +} + +memcached_return_t memcached_replace(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_REPLACE_START(); + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, REPLACE_OP); + LIBMEMCACHED_MEMCACHED_REPLACE_END(); + return rc; +} + +memcached_return_t memcached_prepend(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, PREPEND_OP); + return rc; +} + +memcached_return_t memcached_append(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, 0, APPEND_OP); + return rc; +} + +memcached_return_t memcached_cas(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas) +{ + memcached_return_t rc; + rc= memcached_send(ptr, key, key_length, + key, key_length, value, value_length, + expiration, flags, cas, CAS_OP); + return rc; +} + +memcached_return_t memcached_set_by_key(memcached_st *ptr, + const char *master_key __attribute__((unused)), + size_t master_key_length __attribute__((unused)), + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_SET_START(); + rc= memcached_send(ptr, master_key, master_key_length, + key, key_length, value, value_length, + expiration, flags, 0, SET_OP); + LIBMEMCACHED_MEMCACHED_SET_END(); + return rc; +} + +memcached_return_t memcached_add_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_ADD_START(); + rc= memcached_send(ptr, master_key, master_key_length, + key, key_length, value, value_length, + expiration, flags, 0, ADD_OP); + LIBMEMCACHED_MEMCACHED_ADD_END(); + return rc; +} + +memcached_return_t memcached_replace_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_REPLACE_START(); + rc= memcached_send(ptr, master_key, master_key_length, + key, key_length, value, value_length, + expiration, flags, 0, REPLACE_OP); + LIBMEMCACHED_MEMCACHED_REPLACE_END(); + return rc; +} + +memcached_return_t memcached_prepend_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, master_key, master_key_length, + key, key_length, value, value_length, + expiration, flags, 0, PREPEND_OP); + return rc; +} + +memcached_return_t memcached_append_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags) +{ + memcached_return_t rc; + rc= memcached_send(ptr, master_key, master_key_length, + key, key_length, value, value_length, + expiration, flags, 0, APPEND_OP); + return rc; +} + +memcached_return_t memcached_cas_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas) +{ + memcached_return_t rc; + rc= memcached_send(ptr, master_key, master_key_length, + key, key_length, value, value_length, + expiration, flags, cas, CAS_OP); + return rc; +} + +static inline uint8_t get_com_code(memcached_storage_action_t verb, bool noreply) +{ + /* 0 isn't a value we want, but GCC 4.2 seems to think ret can otherwise + * be used uninitialized in this function. FAIL */ + uint8_t ret= 0; + + if (noreply) + switch (verb) + { + case SET_OP: + ret=PROTOCOL_BINARY_CMD_SETQ; + break; + case ADD_OP: + ret=PROTOCOL_BINARY_CMD_ADDQ; + break; + case CAS_OP: /* FALLTHROUGH */ + case REPLACE_OP: + ret=PROTOCOL_BINARY_CMD_REPLACEQ; + break; + case APPEND_OP: + ret=PROTOCOL_BINARY_CMD_APPENDQ; + break; + case PREPEND_OP: + ret=PROTOCOL_BINARY_CMD_PREPENDQ; + break; + default: + WATCHPOINT_ASSERT(verb); + break; + } + else + switch (verb) + { + case SET_OP: + ret=PROTOCOL_BINARY_CMD_SET; + break; + case ADD_OP: + ret=PROTOCOL_BINARY_CMD_ADD; + break; + case CAS_OP: /* FALLTHROUGH */ + case REPLACE_OP: + ret=PROTOCOL_BINARY_CMD_REPLACE; + break; + case APPEND_OP: + ret=PROTOCOL_BINARY_CMD_APPEND; + break; + case PREPEND_OP: + ret=PROTOCOL_BINARY_CMD_PREPEND; + break; + default: + WATCHPOINT_ASSERT(verb); + break; + } + + return ret; +} + + + +static memcached_return_t memcached_send_binary(memcached_st *ptr, + const char *master_key, + size_t master_key_length, + const char *key, + size_t key_length, + const char *value, + size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas, + memcached_storage_action_t verb) +{ + uint8_t flush; + protocol_binary_request_set request= {.bytes= {0}}; + size_t send_length= sizeof(request.bytes); + uint32_t server_key= memcached_generate_hash(ptr, master_key, + master_key_length); + memcached_server_st *server= &ptr->hosts[server_key]; + bool noreply= server->root->flags.no_reply; + + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= get_com_code(verb, noreply); + request.message.header.request.keylen= htons((uint16_t)key_length); + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + if (verb == APPEND_OP || verb == PREPEND_OP) + send_length -= 8; /* append & prepend does not contain extras! */ + else + { + request.message.header.request.extlen= 8; + request.message.body.flags= htonl(flags); + request.message.body.expiration= htonl((uint32_t)expiration); + } + + request.message.header.request.bodylen= htonl((uint32_t) (key_length + value_length + + request.message.header.request.extlen)); + + if (cas) + request.message.header.request.cas= htonll(cas); + + flush= (uint8_t) ((server->root->flags.buffer_requests && verb == SET_OP) ? 0 : 1); + + if (server->root->flags.use_udp && !flush) + { + size_t cmd_size= send_length + key_length + value_length; + + if (cmd_size > MAX_UDP_DATAGRAM_LENGTH - UDP_DATAGRAM_HEADER_LENGTH) + return MEMCACHED_WRITE_FAILURE; + if (cmd_size + server->write_buffer_offset > MAX_UDP_DATAGRAM_LENGTH) + memcached_io_write(server,NULL,0, 1); + } + + /* write the header */ + if ((memcached_do(server, (const char*)request.bytes, send_length, 0) != MEMCACHED_SUCCESS) || + (memcached_io_write(server, key, key_length, 0) == -1) || + (memcached_io_write(server, value, value_length, (char) flush) == -1)) + { + memcached_io_reset(server); + return MEMCACHED_WRITE_FAILURE; + } + + unlikely (verb == SET_OP && ptr->number_of_replicas > 0) + { + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ; + + for (uint32_t x= 0; x < ptr->number_of_replicas; x++) + { + ++server_key; + if (server_key == ptr->number_of_hosts) + server_key= 0; + + memcached_server_st *srv= &ptr->hosts[server_key]; + if ((memcached_do(srv, (const char*)request.bytes, + send_length, 0) != MEMCACHED_SUCCESS) || + (memcached_io_write(srv, key, key_length, 0) == -1) || + (memcached_io_write(srv, value, value_length, (char) flush) == -1)) + memcached_io_reset(srv); + else + memcached_server_response_decrement(srv); + } + } + + if (flush == 0) + return MEMCACHED_BUFFERED; + + if (noreply) + return MEMCACHED_SUCCESS; + + return memcached_response(server, NULL, 0, NULL); +} + diff --git a/libmemcached/storage.h b/libmemcached/storage.h new file mode 100644 index 00000000..ec982ba0 --- /dev/null +++ b/libmemcached/storage.h @@ -0,0 +1,110 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Storage related functions, aka set, replace,.. + * + */ + +#ifndef __MEMCACHED_STORAGE_H__ +#define __MEMCACHED_STORAGE_H__ + +#include "libmemcached/memcached.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* All of the functions for adding data to the server */ +LIBMEMCACHED_API +memcached_return_t memcached_set(memcached_st *ptr, const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); +LIBMEMCACHED_API +memcached_return_t memcached_add(memcached_st *ptr, const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); +LIBMEMCACHED_API +memcached_return_t memcached_replace(memcached_st *ptr, const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); +LIBMEMCACHED_API +memcached_return_t memcached_append(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); +LIBMEMCACHED_API +memcached_return_t memcached_prepend(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); +LIBMEMCACHED_API +memcached_return_t memcached_cas(memcached_st *ptr, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas); + +LIBMEMCACHED_API +memcached_return_t memcached_set_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); + +LIBMEMCACHED_API +memcached_return_t memcached_add_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); + +LIBMEMCACHED_API +memcached_return_t memcached_replace_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); + +LIBMEMCACHED_API +memcached_return_t memcached_prepend_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); + +LIBMEMCACHED_API +memcached_return_t memcached_append_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags); + +LIBMEMCACHED_API +memcached_return_t memcached_cas_by_key(memcached_st *ptr, + const char *master_key, size_t master_key_length, + const char *key, size_t key_length, + const char *value, size_t value_length, + time_t expiration, + uint32_t flags, + uint64_t cas); + +#ifdef __cplusplus +} +#endif + +#endif /* __MEMCACHED_STORAGE_H__ */ diff --git a/libmemcached/strerror.c b/libmemcached/strerror.c new file mode 100644 index 00000000..f1d651c9 --- /dev/null +++ b/libmemcached/strerror.c @@ -0,0 +1,90 @@ +#include "common.h" + +const char *memcached_strerror(memcached_st *ptr __attribute__((unused)), memcached_return_t rc) +{ + switch (rc) + { + case MEMCACHED_SUCCESS: + return "SUCCESS"; + case MEMCACHED_FAILURE: + return "FAILURE"; + case MEMCACHED_HOST_LOOKUP_FAILURE: + return "HOSTNAME LOOKUP FAILURE"; + case MEMCACHED_CONNECTION_FAILURE: + return "CONNECTION FAILURE"; + case MEMCACHED_CONNECTION_BIND_FAILURE: + return "CONNECTION BIND FAILURE"; + case MEMCACHED_READ_FAILURE: + return "READ FAILURE"; + case MEMCACHED_UNKNOWN_READ_FAILURE: + return "UNKNOWN READ FAILURE"; + case MEMCACHED_PROTOCOL_ERROR: + return "PROTOCOL ERROR"; + case MEMCACHED_CLIENT_ERROR: + return "CLIENT ERROR"; + case MEMCACHED_SERVER_ERROR: + return "SERVER ERROR"; + case MEMCACHED_WRITE_FAILURE: + return "WRITE FAILURE"; + case MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE: + return "CONNECTION SOCKET CREATE FAILURE"; + case MEMCACHED_DATA_EXISTS: + return "CONNECTION DATA EXISTS"; + case MEMCACHED_DATA_DOES_NOT_EXIST: + return "CONNECTION DATA DOES NOT EXIST"; + case MEMCACHED_NOTSTORED: + return "NOT STORED"; + case MEMCACHED_STORED: + return "STORED"; + case MEMCACHED_NOTFOUND: + return "NOT FOUND"; + case MEMCACHED_MEMORY_ALLOCATION_FAILURE: + return "MEMORY ALLOCATION FAILURE"; + case MEMCACHED_PARTIAL_READ: + return "PARTIAL READ"; + case MEMCACHED_SOME_ERRORS: + return "SOME ERRORS WERE REPORTED"; + case MEMCACHED_NO_SERVERS: + return "NO SERVERS DEFINED"; + case MEMCACHED_END: + return "SERVER END"; + case MEMCACHED_DELETED: + return "SERVER DELETE"; + case MEMCACHED_VALUE: + return "SERVER VALUE"; + case MEMCACHED_STAT: + return "STAT VALUE"; + case MEMCACHED_ITEM: + return "ITEM VALUE"; + case MEMCACHED_ERRNO: + return "SYSTEM ERROR"; + case MEMCACHED_FAIL_UNIX_SOCKET: + return "COULD NOT OPEN UNIX SOCKET"; + case MEMCACHED_NOT_SUPPORTED: + return "ACTION NOT SUPPORTED"; + case MEMCACHED_FETCH_NOTFINISHED: + return "FETCH WAS NOT COMPLETED"; + case MEMCACHED_NO_KEY_PROVIDED: + return "A KEY LENGTH OF ZERO WAS PROVIDED"; + case MEMCACHED_BUFFERED: + return "ACTION QUEUED"; + case MEMCACHED_TIMEOUT: + return "A TIMEOUT OCCURRED"; + case MEMCACHED_BAD_KEY_PROVIDED: + return "A BAD KEY WAS PROVIDED/CHARACTERS OUT OF RANGE"; + case MEMCACHED_INVALID_HOST_PROTOCOL: + return "THE HOST TRANSPORT PROTOCOL DOES NOT MATCH THAT OF THE CLIENT"; + case MEMCACHED_SERVER_MARKED_DEAD: + return "SERVER IS MARKED DEAD"; + case MEMCACHED_UNKNOWN_STAT_KEY: + return "ENCOUNTERED AN UNKNOWN STAT KEY"; + case MEMCACHED_E2BIG: + return "ITEM TOO BIG"; + case MEMCACHED_INVALID_ARGUMENTS: + return "INVALID ARGUMENTS"; + case MEMCACHED_MAXIMUM_RETURN: + return "Gibberish returned!"; + default: + return "Gibberish returned!"; + } +} diff --git a/libmemcached/string.c b/libmemcached/string.c new file mode 100644 index 00000000..838ca416 --- /dev/null +++ b/libmemcached/string.c @@ -0,0 +1,171 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: String structure used for libmemcached. + * + */ + +#include "common.h" + +inline static memcached_return_t _string_check(memcached_string_st *string, size_t need) +{ + if (need && need > (size_t)(string->current_size - (size_t)(string->end - string->string))) + { + size_t current_offset= (size_t) (string->end - string->string); + char *new_value; + size_t adjust; + size_t new_size; + + /* This is the block multiplier. To keep it larger and surive division errors we must round it up */ + adjust= (need - (size_t)(string->current_size - (size_t)(string->end - string->string))) / string->block_size; + adjust++; + + new_size= sizeof(char) * (size_t)((adjust * string->block_size) + string->current_size); + /* Test for overflow */ + if (new_size < need) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + new_value= string->root->call_realloc(string->root, string->string, new_size); + + if (new_value == NULL) + return MEMCACHED_MEMORY_ALLOCATION_FAILURE; + + string->string= new_value; + string->end= string->string + current_offset; + + string->current_size+= (string->block_size * adjust); + } + + return MEMCACHED_SUCCESS; +} + +memcached_string_st *memcached_string_create(memcached_st *memc, memcached_string_st *string, size_t initial_size) +{ + memcached_return_t rc; + + /* Saving malloc calls :) */ + if (string) + { + WATCHPOINT_ASSERT(memc->options.is_safe && string->options.is_initialized == false); + + memset(string, 0, sizeof(memcached_string_st)); + } + else + { + string= memc->call_calloc(memc, 1, sizeof(memcached_string_st)); + + if (string == NULL) + { + return NULL; + } + + string->options.is_allocated= true; + } + string->block_size= MEMCACHED_BLOCK_SIZE; + string->root= memc; + + rc= _string_check(string, initial_size); + if (rc != MEMCACHED_SUCCESS) + { + memc->call_free(memc, string); + return NULL; + } + + string->options.is_initialized= true; + + WATCHPOINT_ASSERT(string->string == string->end); + + return string; +} + +memcached_return_t memcached_string_append_character(memcached_string_st *string, + char character) +{ + memcached_return_t rc; + + rc= _string_check(string, 1); + + if (rc != MEMCACHED_SUCCESS) + return rc; + + *string->end= character; + string->end++; + + return MEMCACHED_SUCCESS; +} + +memcached_return_t memcached_string_append(memcached_string_st *string, + const char *value, size_t length) +{ + memcached_return_t rc; + + rc= _string_check(string, length); + + if (rc != MEMCACHED_SUCCESS) + return rc; + + WATCHPOINT_ASSERT(length <= string->current_size); + WATCHPOINT_ASSERT(string->string); + WATCHPOINT_ASSERT(string->end >= string->string); + + memcpy(string->end, value, length); + string->end+= length; + + return MEMCACHED_SUCCESS; +} + +char *memcached_string_c_copy(memcached_string_st *string) +{ + char *c_ptr; + + if (memcached_string_length(string) == 0) + return NULL; + + c_ptr= string->root->call_malloc(string->root, (memcached_string_length(string)+1) * sizeof(char)); + + if (c_ptr == NULL) + return NULL; + + memcpy(c_ptr, memcached_string_value(string), memcached_string_length(string)); + c_ptr[memcached_string_length(string)]= 0; + + return c_ptr; +} + +memcached_return_t memcached_string_reset(memcached_string_st *string) +{ + string->end= string->string; + + return MEMCACHED_SUCCESS; +} + +void memcached_string_free(memcached_string_st *ptr) +{ + if (ptr == NULL) + return; + + if (ptr->string) + { + ptr->root->call_free(ptr->root, ptr->string); + } + + if (memcached_is_allocated(ptr)) + { + ptr->root->call_free(ptr->root, ptr); + } + else + { + ptr->options.is_initialized= false; + memset(ptr, 0, sizeof(memcached_string_st)); + } +} + +memcached_return_t memcached_string_check(memcached_string_st *string, size_t need) +{ + return _string_check(string, need); +} + diff --git a/libmemcached/string.h b/libmemcached/string.h new file mode 100644 index 00000000..d93bc0d2 --- /dev/null +++ b/libmemcached/string.h @@ -0,0 +1,62 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: String structure used for libmemcached. + * + */ + +#ifndef __MEMCACHED_STRING_H__ +#define __MEMCACHED_STRING_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct memcached_string_st { + memcached_st *root; + char *end; + char *string; + size_t current_size; + size_t block_size; + struct { + bool is_allocated:1; + bool is_initialized:1; + } options; +}; + +#define memcached_string_length(A) (size_t)((A)->end - (A)->string) +#define memcached_string_set_length(A, B) (A)->end= (A)->string + B +#define memcached_string_size(A) (A)->current_size +#define memcached_string_value(A) (A)->string + +LIBMEMCACHED_API +memcached_string_st *memcached_string_create(memcached_st *ptr, + memcached_string_st *string, + size_t initial_size); +LIBMEMCACHED_API +memcached_return_t memcached_string_check(memcached_string_st *string, size_t need); + +LIBMEMCACHED_API +char *memcached_string_c_copy(memcached_string_st *string); + +LIBMEMCACHED_API +memcached_return_t memcached_string_append_character(memcached_string_st *string, + char character); +LIBMEMCACHED_API +memcached_return_t memcached_string_append(memcached_string_st *string, + const char *value, size_t length); +LIBMEMCACHED_API +memcached_return_t memcached_string_reset(memcached_string_st *string); + +LIBMEMCACHED_API +void memcached_string_free(memcached_string_st *string); + +#ifdef __cplusplus +} +#endif + +#endif /* __MEMCACHED_STRING_H__ */ diff --git a/libmemcached/types.h b/libmemcached/types.h new file mode 100644 index 00000000..58f0887a --- /dev/null +++ b/libmemcached/types.h @@ -0,0 +1,76 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Types for libmemcached + * + */ + +#ifndef LIBMEMCACHED_MEMCACHED_TYPES_H +#define LIBMEMCACHED_MEMCACHED_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct memcached_st memcached_st; +typedef struct memcached_stat_st memcached_stat_st; +typedef struct memcached_analysis_st memcached_analysis_st; +typedef struct memcached_result_st memcached_result_st; +typedef struct memcached_string_st memcached_string_st; +typedef struct memcached_server_st memcached_server_st; +typedef struct memcached_continuum_item_st memcached_continuum_item_st; +typedef memcached_return_t (*memcached_clone_fn)(memcached_st *parent, memcached_st *clone); +typedef memcached_return_t (*memcached_cleanup_fn)(memcached_st *ptr); +typedef void (*memcached_free_fn)(memcached_st *ptr, void *mem); +typedef void *(*memcached_malloc_fn)(memcached_st *ptr, const size_t size); +typedef void *(*memcached_realloc_fn)(memcached_st *ptr, void *mem, const size_t size); +typedef void *(*memcached_calloc_fn)(memcached_st *ptr, size_t nelem, const size_t elsize); +typedef memcached_return_t (*memcached_execute_fn)(memcached_st *ptr, memcached_result_st *result, void *context); +typedef memcached_return_t (*memcached_server_fn)(memcached_st *ptr, memcached_server_st *server, void *context); +typedef memcached_return_t (*memcached_trigger_key_fn)(memcached_st *ptr, + const char *key, size_t key_length, + memcached_result_st *result); +typedef memcached_return_t (*memcached_trigger_delete_key_fn)(memcached_st *ptr, + const char *key, size_t key_length); + +typedef memcached_return_t (*memcached_dump_fn)(memcached_st *ptr, + const char *key, size_t key_length, void *context); + +typedef struct { + memcached_execute_fn *callback; + void *context; + uint32_t number_of_callback; +} memcached_callback_st; + + +/** + @note The following definitions are just here for backwards compatibility. +*/ +typedef memcached_return_t memcached_return; +typedef memcached_server_distribution_t memcached_server_distribution; +typedef memcached_behavior_t memcached_behavior; +typedef memcached_callback_t memcached_callback; +typedef memcached_hash_t memcached_hash; +typedef memcached_connection_t memcached_connection; +typedef memcached_clone_fn memcached_clone_func; +typedef memcached_cleanup_fn memcached_cleanup_func; +typedef memcached_free_fn memcached_free_function; +typedef memcached_malloc_fn memcached_malloc_function; +typedef memcached_realloc_fn memcached_realloc_function; +typedef memcached_calloc_fn memcached_calloc_function; +typedef memcached_execute_fn memcached_execute_function; +typedef memcached_server_fn memcached_server_function; +typedef memcached_trigger_key_fn memcached_trigger_key; +typedef memcached_trigger_delete_key_fn memcached_trigger_delete_key; +typedef memcached_dump_fn memcached_dump_func; + + +#ifdef __cplusplus +} +#endif + +#endif /* LIBMEMCACHED_MEMCACHED_TYPES_H */ diff --git a/libmemcached/util/memcached_pool.c b/libmemcached/util/memcached_pool.c deleted file mode 100644 index fac7d2d3..00000000 --- a/libmemcached/util/memcached_pool.c +++ /dev/null @@ -1,246 +0,0 @@ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include "libmemcached/common.h" -#include "libmemcached/memcached_pool.h" - -#include -#include - -struct memcached_pool_st -{ - pthread_mutex_t mutex; - pthread_cond_t cond; - memcached_st *master; - memcached_st **mmc; - int firstfree; - uint32_t size; - uint32_t current_size; - char *version; -}; - -static memcached_return_t mutex_enter(pthread_mutex_t *mutex) -{ - int ret; - do - ret= pthread_mutex_lock(mutex); - while (ret == -1 && errno == EINTR); - - return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; -} - -static memcached_return_t mutex_exit(pthread_mutex_t *mutex) { - int ret; - do - ret= pthread_mutex_unlock(mutex); - while (ret == -1 && errno == EINTR); - - return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; -} - -/** - * Grow the connection pool by creating a connection structure and clone the - * original memcached handle. - */ -static int grow_pool(memcached_pool_st* pool) { - memcached_st *obj= calloc(1, sizeof(*obj)); - if (obj == NULL) - return -1; - - if (memcached_clone(obj, pool->master) == NULL) - { - free(obj); - return -1; - } - - pool->mmc[++pool->firstfree] = obj; - pool->current_size++; - - return 0; -} - -memcached_pool_st *memcached_pool_create(memcached_st* mmc, - uint32_t initial, uint32_t max) -{ - memcached_pool_st* ret = NULL; - memcached_pool_st object = { .mutex = PTHREAD_MUTEX_INITIALIZER, - .cond = PTHREAD_COND_INITIALIZER, - .master = mmc, - .mmc = calloc(max, sizeof(memcached_st*)), - .firstfree = -1, - .size = max, - .current_size = 0 }; - - if (object.mmc != NULL) - { - ret= calloc(1, sizeof(*ret)); - if (ret == NULL) - { - free(object.mmc); - return NULL; - } - - *ret = object; - - /* Try to create the initial size of the pool. An allocation failure at - * this time is not fatal.. - */ - for (unsigned int ii=0; ii < initial; ++ii) - if (grow_pool(ret) == -1) - break; - } - - return ret; -} - -memcached_st* memcached_pool_destroy(memcached_pool_st* pool) -{ - memcached_st *ret = pool->master; - - for (int xx= 0; xx <= pool->firstfree; ++xx) - { - memcached_free(pool->mmc[xx]); - free(pool->mmc[xx]); - pool->mmc[xx] = NULL; - } - - pthread_mutex_destroy(&pool->mutex); - pthread_cond_destroy(&pool->cond); - free(pool->mmc); - free(pool); - - return ret; -} - -memcached_st* memcached_pool_pop(memcached_pool_st* pool, - bool block, - memcached_return_t *rc) -{ - memcached_st *ret= NULL; - if ((*rc= mutex_enter(&pool->mutex)) != MEMCACHED_SUCCESS) - return NULL; - - do - { - if (pool->firstfree > -1) - ret= pool->mmc[pool->firstfree--]; - else if (pool->current_size == pool->size) - { - if (!block) - { - *rc= mutex_exit(&pool->mutex); - return NULL; - } - - if (pthread_cond_wait(&pool->cond, &pool->mutex) == -1) - { - int err = errno; - mutex_exit(&pool->mutex); - errno = err; - *rc= MEMCACHED_ERRNO; - return NULL; - } - } - else if (grow_pool(pool) == -1) - { - *rc= mutex_exit(&pool->mutex); - return NULL; - } - } - while (ret == NULL); - - *rc= mutex_exit(&pool->mutex); - - return ret; -} - -memcached_return_t memcached_pool_push(memcached_pool_st* pool, - memcached_st *mmc) -{ - memcached_return_t rc= mutex_enter(&pool->mutex); - - if (rc != MEMCACHED_SUCCESS) - return rc; - - char* version = memcached_get_user_data(mmc); - /* Someone updated the behavior on the object.. */ - if (version != pool->version) - { - memcached_free(mmc); - memset(mmc, 0, sizeof(*mmc)); - if (memcached_clone(mmc, pool->master) == NULL) - { - rc= MEMCACHED_SOME_ERRORS; - } - } - - pool->mmc[++pool->firstfree]= mmc; - - if (pool->firstfree == 0 && pool->current_size == pool->size) - { - /* we might have people waiting for a connection.. wake them up :-) */ - pthread_cond_broadcast(&pool->cond); - } - - memcached_return_t rval= mutex_exit(&pool->mutex); - if (rc == MEMCACHED_SOME_ERRORS) - return rc; - - return rval; -} - - -memcached_return_t memcached_pool_behavior_set(memcached_pool_st *pool, - memcached_behavior_t flag, - uint64_t data) -{ - - memcached_return_t rc= mutex_enter(&pool->mutex); - if (rc != MEMCACHED_SUCCESS) - return rc; - - /* update the master */ - rc= memcached_behavior_set(pool->master, flag, data); - if (rc != MEMCACHED_SUCCESS) - { - mutex_exit(&pool->mutex); - return rc; - } - - ++pool->version; - memcached_set_user_data(pool->master, pool->version); - /* update the clones */ - for (int xx= 0; xx <= pool->firstfree; ++xx) - { - rc= memcached_behavior_set(pool->mmc[xx], flag, data); - if (rc == MEMCACHED_SUCCESS) - memcached_set_user_data(pool->mmc[xx], pool->version); - else - { - memcached_free(pool->mmc[xx]); - memset(pool->mmc[xx], 0, sizeof(*pool->mmc[xx])); - if (memcached_clone(pool->mmc[xx], pool->master) == NULL) - { - /* I'm not sure what to do in this case.. this would happen - if we fail to push the server list inside the client.. - I should add a testcase for this, but I believe the following - would work, except that you would add a hole in the pool list.. - in theory you could end up with an empty pool.... - */ - free(pool->mmc[xx]); - pool->mmc[xx]= NULL; - } - } - } - - return mutex_exit(&pool->mutex); -} - -memcached_return_t memcached_pool_behavior_get(memcached_pool_st *pool, - memcached_behavior_t flag, - uint64_t *value) -{ - memcached_return_t rc= mutex_enter(&pool->mutex); - if (rc != MEMCACHED_SUCCESS) - return rc; - *value= memcached_behavior_get(pool->master, flag); - return mutex_exit(&pool->mutex); -} diff --git a/libmemcached/util/pool.c b/libmemcached/util/pool.c new file mode 100644 index 00000000..7cb25177 --- /dev/null +++ b/libmemcached/util/pool.c @@ -0,0 +1,257 @@ +/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +#include "libmemcached/common.h" +#include "libmemcached/pool.h" + +#include +#include + +struct memcached_pool_st +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + memcached_st *master; + memcached_st **mmc; + int firstfree; + uint32_t size; + uint32_t current_size; + char *version; +}; + +static memcached_return_t mutex_enter(pthread_mutex_t *mutex) +{ + int ret; + do + ret= pthread_mutex_lock(mutex); + while (ret == -1 && errno == EINTR); + + return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; +} + +static memcached_return_t mutex_exit(pthread_mutex_t *mutex) +{ + int ret; + do + ret= pthread_mutex_unlock(mutex); + while (ret == -1 && errno == EINTR); + + return (ret == -1) ? MEMCACHED_ERRNO : MEMCACHED_SUCCESS; +} + +/** + * Grow the connection pool by creating a connection structure and clone the + * original memcached handle. + */ +static int grow_pool(memcached_pool_st* pool) +{ + memcached_st *obj= calloc(1, sizeof(*obj)); + + if (obj == NULL) + return -1; + + if (memcached_clone(obj, pool->master) == NULL) + { + free(obj); + return -1; + } + + pool->mmc[++pool->firstfree] = obj; + pool->current_size++; + + return 0; +} + +memcached_pool_st *memcached_pool_create(memcached_st* mmc, + uint32_t initial, uint32_t max) +{ + memcached_pool_st* ret = NULL; + memcached_pool_st object = { .mutex = PTHREAD_MUTEX_INITIALIZER, + .cond = PTHREAD_COND_INITIALIZER, + .master = mmc, + .mmc = calloc(max, sizeof(memcached_st*)), + .firstfree = -1, + .size = max, + .current_size = 0 }; + + if (object.mmc != NULL) + { + ret= calloc(1, sizeof(*ret)); + if (ret == NULL) + { + free(object.mmc); + return NULL; + } + + *ret = object; + + /* + Try to create the initial size of the pool. An allocation failure at + this time is not fatal.. + */ + for (unsigned int ii= 0; ii < initial; ++ii) + { + if (grow_pool(ret) == -1) + break; + } + } + + return ret; +} + +memcached_st* memcached_pool_destroy(memcached_pool_st* pool) +{ + memcached_st *ret = pool->master; + + for (int xx= 0; xx <= pool->firstfree; ++xx) + { + memcached_free(pool->mmc[xx]); + free(pool->mmc[xx]); + pool->mmc[xx] = NULL; + } + + pthread_mutex_destroy(&pool->mutex); + pthread_cond_destroy(&pool->cond); + free(pool->mmc); + free(pool); + + return ret; +} + +memcached_st* memcached_pool_pop(memcached_pool_st* pool, + bool block, + memcached_return_t *rc) +{ + memcached_st *ret= NULL; + if ((*rc= mutex_enter(&pool->mutex)) != MEMCACHED_SUCCESS) + return NULL; + + do + { + if (pool->firstfree > -1) + ret= pool->mmc[pool->firstfree--]; + else if (pool->current_size == pool->size) + { + if (!block) + { + *rc= mutex_exit(&pool->mutex); + return NULL; + } + + if (pthread_cond_wait(&pool->cond, &pool->mutex) == -1) + { + int err = errno; + mutex_exit(&pool->mutex); + errno = err; + *rc= MEMCACHED_ERRNO; + return NULL; + } + } + else if (grow_pool(pool) == -1) + { + *rc= mutex_exit(&pool->mutex); + return NULL; + } + } + while (ret == NULL); + + *rc= mutex_exit(&pool->mutex); + + return ret; +} + +memcached_return_t memcached_pool_push(memcached_pool_st* pool, + memcached_st *mmc) +{ + memcached_return_t rc= mutex_enter(&pool->mutex); + + if (rc != MEMCACHED_SUCCESS) + return rc; + + char* version= memcached_get_user_data(mmc); + /* Someone updated the behavior on the object.. */ + if (version != pool->version) + { + memcached_free(mmc); + memset(mmc, 0, sizeof(*mmc)); + if (memcached_clone(mmc, pool->master) == NULL) + { + rc= MEMCACHED_SOME_ERRORS; + } + } + + pool->mmc[++pool->firstfree]= mmc; + + if (pool->firstfree == 0 && pool->current_size == pool->size) + { + /* we might have people waiting for a connection.. wake them up :-) */ + pthread_cond_broadcast(&pool->cond); + } + + memcached_return_t rval= mutex_exit(&pool->mutex); + if (rc == MEMCACHED_SOME_ERRORS) + return rc; + + return rval; +} + + +memcached_return_t memcached_pool_behavior_set(memcached_pool_st *pool, + memcached_behavior_t flag, + uint64_t data) +{ + + memcached_return_t rc= mutex_enter(&pool->mutex); + if (rc != MEMCACHED_SUCCESS) + return rc; + + /* update the master */ + rc= memcached_behavior_set(pool->master, flag, data); + if (rc != MEMCACHED_SUCCESS) + { + mutex_exit(&pool->mutex); + return rc; + } + + ++pool->version; + memcached_set_user_data(pool->master, pool->version); + /* update the clones */ + for (int xx= 0; xx <= pool->firstfree; ++xx) + { + rc= memcached_behavior_set(pool->mmc[xx], flag, data); + if (rc == MEMCACHED_SUCCESS) + memcached_set_user_data(pool->mmc[xx], pool->version); + else + { + memcached_free(pool->mmc[xx]); + memset(pool->mmc[xx], 0, sizeof(*pool->mmc[xx])); + if (memcached_clone(pool->mmc[xx], pool->master) == NULL) + { + /* I'm not sure what to do in this case.. this would happen + if we fail to push the server list inside the client.. + I should add a testcase for this, but I believe the following + would work, except that you would add a hole in the pool list.. + in theory you could end up with an empty pool.... + */ + free(pool->mmc[xx]); + pool->mmc[xx]= NULL; + } + } + } + + return mutex_exit(&pool->mutex); +} + +memcached_return_t memcached_pool_behavior_get(memcached_pool_st *pool, + memcached_behavior_t flag, + uint64_t *value) +{ + memcached_return_t rc= mutex_enter(&pool->mutex); + + if (rc != MEMCACHED_SUCCESS) + { + return rc; + } + + *value= memcached_behavior_get(pool->master, flag); + + return mutex_exit(&pool->mutex); +} diff --git a/libmemcached/verbosity.c b/libmemcached/verbosity.c new file mode 100644 index 00000000..3d34880d --- /dev/null +++ b/libmemcached/verbosity.c @@ -0,0 +1,36 @@ +#include "common.h" + +memcached_return_t memcached_verbosity(memcached_st *ptr, unsigned int verbosity) +{ + unsigned int x; + size_t send_length; + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "verbosity %u\r\n", verbosity); + unlikely (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) + return MEMCACHED_WRITE_FAILURE; + + rc= MEMCACHED_SUCCESS; + for (x= 0; x < ptr->number_of_hosts; x++) + { + memcached_return_t rrc; + + rrc= memcached_do(&ptr->hosts[x], buffer, send_length, 1); + if (rrc != MEMCACHED_SUCCESS) + { + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + unlikely (ptr->flags.use_udp) + continue; + + rrc= memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + if (rrc != MEMCACHED_SUCCESS) + rc= MEMCACHED_SOME_ERRORS; + } + + return rc; +} diff --git a/libmemcached/version.c b/libmemcached/version.c new file mode 100644 index 00000000..db2419cf --- /dev/null +++ b/libmemcached/version.c @@ -0,0 +1,112 @@ +#include "common.h" + +const char * memcached_lib_version(void) +{ + return LIBMEMCACHED_VERSION_STRING; +} + +static inline memcached_return_t memcached_version_binary(memcached_st *ptr); +static inline memcached_return_t memcached_version_textual(memcached_st *ptr); + +memcached_return_t memcached_version(memcached_st *ptr) +{ + if (ptr->flags.use_udp) + return MEMCACHED_NOT_SUPPORTED; + + if (ptr->flags.binary_protocol) + return memcached_version_binary(ptr); + else + return memcached_version_textual(ptr); +} + +static inline memcached_return_t memcached_version_textual(memcached_st *ptr) +{ + unsigned int x; + size_t send_length; + memcached_return_t rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + char *response_ptr; + const char *command= "version\r\n"; + + send_length= strlen(command); + + rc= MEMCACHED_SUCCESS; + for (x= 0; x < ptr->number_of_hosts; x++) + { + memcached_return_t rrc; + + rrc= memcached_do(&ptr->hosts[x], command, send_length, 1); + if (rrc != MEMCACHED_SUCCESS) + { + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + rrc= memcached_response(&ptr->hosts[x], buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + if (rrc != MEMCACHED_SUCCESS) + { + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + /* Find the space, and then move one past it to copy version */ + response_ptr= index(buffer, ' '); + response_ptr++; + + ptr->hosts[x].major_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + response_ptr= index(response_ptr, '.'); + response_ptr++; + ptr->hosts[x].minor_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + response_ptr= index(response_ptr, '.'); + response_ptr++; + ptr->hosts[x].micro_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + } + + return rc; +} + +static inline memcached_return_t memcached_version_binary(memcached_st *ptr) +{ + memcached_return_t rc; + unsigned int x; + protocol_binary_request_version request= { .bytes= {0}}; + request.message.header.request.magic= PROTOCOL_BINARY_REQ; + request.message.header.request.opcode= PROTOCOL_BINARY_CMD_VERSION; + request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES; + + rc= MEMCACHED_SUCCESS; + for (x= 0; x < ptr->number_of_hosts; x++) + { + memcached_return_t rrc; + + rrc= memcached_do(&ptr->hosts[x], request.bytes, sizeof(request.bytes), 1); + if (rrc != MEMCACHED_SUCCESS) + { + memcached_io_reset(&ptr->hosts[x]); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + } + + for (x= 0; x < ptr->number_of_hosts; x++) + if (memcached_server_response_count(&ptr->hosts[x]) > 0) + { + memcached_return_t rrc; + char buffer[32]; + char *p; + + rrc= memcached_response(&ptr->hosts[x], buffer, sizeof(buffer), NULL); + if (rrc != MEMCACHED_SUCCESS) + { + memcached_io_reset(&ptr->hosts[x]); + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + ptr->hosts[x].major_version= (uint8_t)strtol(buffer, &p, 10); + ptr->hosts[x].minor_version= (uint8_t)strtol(p + 1, &p, 10); + ptr->hosts[x].micro_version= (uint8_t)strtol(p + 1, NULL, 10); + } + + return rc; +} diff --git a/libmemcached/watchpoint.h b/libmemcached/watchpoint.h new file mode 100644 index 00000000..d8c593ec --- /dev/null +++ b/libmemcached/watchpoint.h @@ -0,0 +1,45 @@ +/* LibMemcached + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Use and distribution licensed under the BSD license. See + * the COPYING file in the parent directory for full text. + * + * Summary: Localized copy of WATCHPOINT debug symbols + * + */ + +#ifndef LIBMEMCACHED_MEMCACHED_WATCHPOINT_H +#define LIBMEMCACHED_MEMCACHED_WATCHPOINT_H + +/* Some personal debugging functions */ +#if defined(DEBUG) + +#include + +#define WATCHPOINT fprintf(stderr, "\nWATCHPOINT %s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout); +#define WATCHPOINT_ERROR(A) fprintf(stderr, "\nWATCHPOINT %s:%d %s\n", __FILE__, __LINE__, memcached_strerror(NULL, A));fflush(stdout); +#define WATCHPOINT_IFERROR(A) if(A != MEMCACHED_SUCCESS)fprintf(stderr, "\nWATCHPOINT %s:%d %s\n", __FILE__, __LINE__, memcached_strerror(NULL, A));fflush(stdout); +#define WATCHPOINT_STRING(A) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %s\n", __FILE__, __LINE__,__func__,A);fflush(stdout); +#define WATCHPOINT_STRING_LENGTH(A,B) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %.*s\n", __FILE__, __LINE__,__func__,(int)B,A);fflush(stdout); +#define WATCHPOINT_NUMBER(A) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %zu\n", __FILE__, __LINE__,__func__,(size_t)(A));fflush(stdout); +#define WATCHPOINT_ERRNO(A) fprintf(stderr, "\nWATCHPOINT %s:%d (%s) %s\n", __FILE__, __LINE__,__func__, strerror(A));fflush(stdout); +#define WATCHPOINT_ASSERT_PRINT(A,B,C) if(!(A)){fprintf(stderr, "\nWATCHPOINT ASSERT %s:%d (%s) ", __FILE__, __LINE__,__func__);fprintf(stderr, (B),(C));fprintf(stderr,"\n");fflush(stdout);}assert((A)); +#define WATCHPOINT_ASSERT(A) assert((A)); +#define WATCHPOINT_ASSERT_INITIALIZED(A) (memcached_is_initialized((A)); + +#else + +#define WATCHPOINT +#define WATCHPOINT_ERROR(A) +#define WATCHPOINT_IFERROR(A) +#define WATCHPOINT_STRING(A) +#define WATCHPOINT_NUMBER(A) +#define WATCHPOINT_ERRNO(A) +#define WATCHPOINT_ASSERT_PRINT(A,B,C) +#define WATCHPOINT_ASSERT(A) +#define WATCHPOINT_ASSERT_INITIALIZED(A) + +#endif /* DEBUG */ + +#endif /* LIBMEMCACHED_MEMCACHED_WATCHPOINT_H */