freebsd-src/contrib/apr-util/memcache/apr_memcache.c
Peter Wemm 937a200089 Introduce svnlite so that we can check out our source code again.
This is actually a fully functional build except:
* All internal shared libraries are static linked to make sure there
  is no interference with ports (and to reduce build time).
* It does not have the python/perl/etc plugin or API support.
* By default, it installs as "svnlite" rather than "svn".
* If WITH_SVN added in make.conf, you get "svn".
* If WITHOUT_SVNLITE is in make.conf, this is completely disabled.

To be absolutely clear, this is not intended for any use other than
checking out freebsd source and committing, like we once did with cvs.

It should be usable for small scale local repositories that don't
need the python/perl plugin architecture.
2013-06-18 02:53:45 +00:00

1700 lines
47 KiB
C

/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_memcache.h"
#include "apr_poll.h"
#include "apr_version.h"
#include <stdlib.h>
#define BUFFER_SIZE 512
struct apr_memcache_conn_t
{
char *buffer;
apr_size_t blen;
apr_pool_t *p;
apr_pool_t *tp;
apr_socket_t *sock;
apr_bucket_brigade *bb;
apr_bucket_brigade *tb;
apr_memcache_server_t *ms;
};
/* Strings for Client Commands */
#define MC_EOL "\r\n"
#define MC_EOL_LEN (sizeof(MC_EOL)-1)
#define MC_WS " "
#define MC_WS_LEN (sizeof(MC_WS)-1)
#define MC_GET "get "
#define MC_GET_LEN (sizeof(MC_GET)-1)
#define MC_SET "set "
#define MC_SET_LEN (sizeof(MC_SET)-1)
#define MC_ADD "add "
#define MC_ADD_LEN (sizeof(MC_ADD)-1)
#define MC_REPLACE "replace "
#define MC_REPLACE_LEN (sizeof(MC_REPLACE)-1)
#define MC_DELETE "delete "
#define MC_DELETE_LEN (sizeof(MC_DELETE)-1)
#define MC_INCR "incr "
#define MC_INCR_LEN (sizeof(MC_INCR)-1)
#define MC_DECR "decr "
#define MC_DECR_LEN (sizeof(MC_DECR)-1)
#define MC_VERSION "version"
#define MC_VERSION_LEN (sizeof(MC_VERSION)-1)
#define MC_STATS "stats"
#define MC_STATS_LEN (sizeof(MC_STATS)-1)
#define MC_QUIT "quit"
#define MC_QUIT_LEN (sizeof(MC_QUIT)-1)
/* Strings for Server Replies */
#define MS_STORED "STORED"
#define MS_STORED_LEN (sizeof(MS_STORED)-1)
#define MS_NOT_STORED "NOT_STORED"
#define MS_NOT_STORED_LEN (sizeof(MS_NOT_STORED)-1)
#define MS_DELETED "DELETED"
#define MS_DELETED_LEN (sizeof(MS_DELETED)-1)
#define MS_NOT_FOUND "NOT_FOUND"
#define MS_NOT_FOUND_LEN (sizeof(MS_NOT_FOUND)-1)
#define MS_VALUE "VALUE"
#define MS_VALUE_LEN (sizeof(MS_VALUE)-1)
#define MS_ERROR "ERROR"
#define MS_ERROR_LEN (sizeof(MS_ERROR)-1)
#define MS_VERSION "VERSION"
#define MS_VERSION_LEN (sizeof(MS_VERSION)-1)
#define MS_STAT "STAT"
#define MS_STAT_LEN (sizeof(MS_STAT)-1)
#define MS_END "END"
#define MS_END_LEN (sizeof(MS_END)-1)
/** Server and Query Structure for a multiple get */
struct cache_server_query_t {
apr_memcache_server_t* ms;
apr_memcache_conn_t* conn;
struct iovec* query_vec;
apr_int32_t query_vec_count;
};
#define MULT_GET_TIMEOUT 50000
static apr_status_t make_server_dead(apr_memcache_t *mc, apr_memcache_server_t *ms)
{
#if APR_HAS_THREADS
apr_thread_mutex_lock(ms->lock);
#endif
ms->status = APR_MC_SERVER_DEAD;
ms->btime = apr_time_now();
#if APR_HAS_THREADS
apr_thread_mutex_unlock(ms->lock);
#endif
return APR_SUCCESS;
}
static apr_status_t make_server_live(apr_memcache_t *mc, apr_memcache_server_t *ms)
{
ms->status = APR_MC_SERVER_LIVE;
return APR_SUCCESS;
}
APU_DECLARE(apr_status_t) apr_memcache_add_server(apr_memcache_t *mc, apr_memcache_server_t *ms)
{
apr_status_t rv = APR_SUCCESS;
if(mc->ntotal >= mc->nalloc) {
return APR_ENOMEM;
}
mc->live_servers[mc->ntotal] = ms;
mc->ntotal++;
make_server_live(mc, ms);
return rv;
}
static apr_status_t mc_version_ping(apr_memcache_server_t *ms);
APU_DECLARE(apr_memcache_server_t *)
apr_memcache_find_server_hash(apr_memcache_t *mc, const apr_uint32_t hash)
{
if (mc->server_func) {
return mc->server_func(mc->server_baton, mc, hash);
}
else {
return apr_memcache_find_server_hash_default(NULL, mc, hash);
}
}
APU_DECLARE(apr_memcache_server_t *)
apr_memcache_find_server_hash_default(void *baton, apr_memcache_t *mc,
const apr_uint32_t hash)
{
apr_memcache_server_t *ms = NULL;
apr_uint32_t h = hash ? hash : 1;
apr_uint32_t i = 0;
apr_time_t curtime = 0;
if(mc->ntotal == 0) {
return NULL;
}
do {
ms = mc->live_servers[h % mc->ntotal];
if(ms->status == APR_MC_SERVER_LIVE) {
break;
}
else {
if (curtime == 0) {
curtime = apr_time_now();
}
#if APR_HAS_THREADS
apr_thread_mutex_lock(ms->lock);
#endif
/* Try the the dead server, every 5 seconds */
if (curtime - ms->btime > apr_time_from_sec(5)) {
if (mc_version_ping(ms) == APR_SUCCESS) {
ms->btime = curtime;
make_server_live(mc, ms);
#if APR_HAS_THREADS
apr_thread_mutex_unlock(ms->lock);
#endif
break;
}
}
#if APR_HAS_THREADS
apr_thread_mutex_unlock(ms->lock);
#endif
}
h++;
i++;
} while(i < mc->ntotal);
if (i == mc->ntotal) {
ms = NULL;
}
return ms;
}
APU_DECLARE(apr_memcache_server_t *) apr_memcache_find_server(apr_memcache_t *mc, const char *host, apr_port_t port)
{
int i;
for (i = 0; i < mc->ntotal; i++) {
if (strcmp(mc->live_servers[i]->host, host) == 0
&& mc->live_servers[i]->port == port) {
return mc->live_servers[i];
}
}
return NULL;
}
static apr_status_t ms_find_conn(apr_memcache_server_t *ms, apr_memcache_conn_t **conn)
{
apr_status_t rv;
apr_bucket_alloc_t *balloc;
apr_bucket *e;
#if APR_HAS_THREADS
rv = apr_reslist_acquire(ms->conns, (void **)conn);
#else
*conn = ms->conn;
rv = APR_SUCCESS;
#endif
if (rv != APR_SUCCESS) {
return rv;
}
balloc = apr_bucket_alloc_create((*conn)->tp);
(*conn)->bb = apr_brigade_create((*conn)->tp, balloc);
(*conn)->tb = apr_brigade_create((*conn)->tp, balloc);
e = apr_bucket_socket_create((*conn)->sock, balloc);
APR_BRIGADE_INSERT_TAIL((*conn)->bb, e);
return rv;
}
static apr_status_t ms_bad_conn(apr_memcache_server_t *ms, apr_memcache_conn_t *conn)
{
#if APR_HAS_THREADS
return apr_reslist_invalidate(ms->conns, conn);
#else
return APR_SUCCESS;
#endif
}
static apr_status_t ms_release_conn(apr_memcache_server_t *ms, apr_memcache_conn_t *conn)
{
apr_pool_clear(conn->tp);
#if APR_HAS_THREADS
return apr_reslist_release(ms->conns, conn);
#else
return APR_SUCCESS;
#endif
}
APU_DECLARE(apr_status_t) apr_memcache_enable_server(apr_memcache_t *mc, apr_memcache_server_t *ms)
{
apr_status_t rv = APR_SUCCESS;
if (ms->status == APR_MC_SERVER_LIVE) {
return rv;
}
rv = make_server_live(mc, ms);
return rv;
}
APU_DECLARE(apr_status_t) apr_memcache_disable_server(apr_memcache_t *mc, apr_memcache_server_t *ms)
{
return make_server_dead(mc, ms);
}
static apr_status_t conn_connect(apr_memcache_conn_t *conn)
{
apr_status_t rv = APR_SUCCESS;
apr_sockaddr_t *sa;
rv = apr_sockaddr_info_get(&sa, conn->ms->host, APR_INET, conn->ms->port, 0, conn->p);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_socket_timeout_set(conn->sock, 1 * APR_USEC_PER_SEC);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_socket_connect(conn->sock, sa);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_socket_timeout_set(conn->sock, -1);
if (rv != APR_SUCCESS) {
return rv;
}
return rv;
}
static apr_status_t
mc_conn_construct(void **conn_, void *params, apr_pool_t *pool)
{
apr_status_t rv = APR_SUCCESS;
apr_memcache_conn_t *conn;
apr_pool_t *np;
apr_pool_t *tp;
apr_memcache_server_t *ms = params;
rv = apr_pool_create(&np, pool);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_pool_create(&tp, np);
if (rv != APR_SUCCESS) {
apr_pool_destroy(np);
return rv;
}
conn = apr_palloc(np, sizeof( apr_memcache_conn_t ));
conn->p = np;
conn->tp = tp;
rv = apr_socket_create(&conn->sock, APR_INET, SOCK_STREAM, 0, np);
if (rv != APR_SUCCESS) {
apr_pool_destroy(np);
return rv;
}
conn->buffer = apr_palloc(conn->p, BUFFER_SIZE);
conn->blen = 0;
conn->ms = ms;
rv = conn_connect(conn);
if (rv != APR_SUCCESS) {
apr_pool_destroy(np);
}
else {
*conn_ = conn;
}
return rv;
}
#if APR_HAS_THREADS
static apr_status_t
mc_conn_destruct(void *conn_, void *params, apr_pool_t *pool)
{
apr_memcache_conn_t *conn = (apr_memcache_conn_t*)conn_;
struct iovec vec[2];
apr_size_t written;
/* send a quit message to the memcached server to be nice about it. */
vec[0].iov_base = MC_QUIT;
vec[0].iov_len = MC_QUIT_LEN;
vec[1].iov_base = MC_EOL;
vec[1].iov_len = MC_EOL_LEN;
/* Return values not checked, since we just want to make it go away. */
apr_socket_sendv(conn->sock, vec, 2, &written);
apr_socket_close(conn->sock);
apr_pool_destroy(conn->p);
return APR_SUCCESS;
}
#endif
APU_DECLARE(apr_status_t) apr_memcache_server_create(apr_pool_t *p,
const char *host, apr_port_t port,
apr_uint32_t min, apr_uint32_t smax,
apr_uint32_t max, apr_uint32_t ttl,
apr_memcache_server_t **ms)
{
apr_status_t rv = APR_SUCCESS;
apr_memcache_server_t *server;
apr_pool_t *np;
rv = apr_pool_create(&np, p);
server = apr_palloc(np, sizeof(apr_memcache_server_t));
server->p = np;
server->host = apr_pstrdup(np, host);
server->port = port;
server->status = APR_MC_SERVER_DEAD;
#if APR_HAS_THREADS
rv = apr_thread_mutex_create(&server->lock, APR_THREAD_MUTEX_DEFAULT, np);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_reslist_create(&server->conns,
min, /* hard minimum */
smax, /* soft maximum */
max, /* hard maximum */
ttl, /* Time to live */
mc_conn_construct, /* Make a New Connection */
mc_conn_destruct, /* Kill Old Connection */
server, np);
if (rv != APR_SUCCESS) {
return rv;
}
apr_reslist_cleanup_order_set(server->conns, APR_RESLIST_CLEANUP_FIRST);
#else
rv = mc_conn_construct((void**)&(server->conn), server, np);
if (rv != APR_SUCCESS) {
return rv;
}
#endif
*ms = server;
return rv;
}
APU_DECLARE(apr_status_t) apr_memcache_create(apr_pool_t *p,
apr_uint16_t max_servers, apr_uint32_t flags,
apr_memcache_t **memcache)
{
apr_status_t rv = APR_SUCCESS;
apr_memcache_t *mc;
mc = apr_palloc(p, sizeof(apr_memcache_t));
mc->p = p;
mc->nalloc = max_servers;
mc->ntotal = 0;
mc->live_servers = apr_palloc(p, mc->nalloc * sizeof(struct apr_memcache_server_t *));
mc->hash_func = NULL;
mc->hash_baton = NULL;
mc->server_func = NULL;
mc->server_baton = NULL;
*memcache = mc;
return rv;
}
/* The crc32 functions and data was originally written by Spencer
* Garrett <srg@quick.com> and was gleaned from the PostgreSQL source
* tree via the files contrib/ltree/crc32.[ch] and from FreeBSD at
* src/usr.bin/cksum/crc32.c.
*/
static const apr_uint32_t crc32tab[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
APU_DECLARE(apr_uint32_t) apr_memcache_hash_crc32(void *baton,
const char *data,
const apr_size_t data_len)
{
apr_uint32_t i;
apr_uint32_t crc;
crc = ~0;
for (i = 0; i < data_len; i++)
crc = (crc >> 8) ^ crc32tab[(crc ^ (data[i])) & 0xff];
return ~crc;
}
APU_DECLARE(apr_uint32_t) apr_memcache_hash_default(void *baton,
const char *data,
const apr_size_t data_len)
{
/* The default Perl Client doesn't actually use just crc32 -- it shifts it again
* like this....
*/
return ((apr_memcache_hash_crc32(baton, data, data_len) >> 16) & 0x7fff);
}
APU_DECLARE(apr_uint32_t) apr_memcache_hash(apr_memcache_t *mc,
const char *data,
const apr_size_t data_len)
{
if (mc->hash_func) {
return mc->hash_func(mc->hash_baton, data, data_len);
}
else {
return apr_memcache_hash_default(NULL, data, data_len);
}
}
static apr_status_t get_server_line(apr_memcache_conn_t *conn)
{
apr_size_t bsize = BUFFER_SIZE;
apr_status_t rv = APR_SUCCESS;
rv = apr_brigade_split_line(conn->tb, conn->bb, APR_BLOCK_READ, BUFFER_SIZE);
if (rv != APR_SUCCESS) {
return rv;
}
rv = apr_brigade_flatten(conn->tb, conn->buffer, &bsize);
if (rv != APR_SUCCESS) {
return rv;
}
conn->blen = bsize;
conn->buffer[bsize] = '\0';
return apr_brigade_cleanup(conn->tb);
}
static apr_status_t storage_cmd_write(apr_memcache_t *mc,
char *cmd,
const apr_size_t cmd_size,
const char *key,
char *data,
const apr_size_t data_size,
apr_uint32_t timeout,
apr_uint16_t flags)
{
apr_uint32_t hash;
apr_memcache_server_t *ms;
apr_memcache_conn_t *conn;
apr_status_t rv;
apr_size_t written;
struct iovec vec[5];
apr_size_t klen;
apr_size_t key_size = strlen(key);
hash = apr_memcache_hash(mc, key, key_size);
ms = apr_memcache_find_server_hash(mc, hash);
if (ms == NULL)
return APR_NOTFOUND;
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
apr_memcache_disable_server(mc, ms);
return rv;
}
/* <command name> <key> <flags> <exptime> <bytes>\r\n<data>\r\n */
vec[0].iov_base = cmd;
vec[0].iov_len = cmd_size;
vec[1].iov_base = (void*)key;
vec[1].iov_len = key_size;
klen = apr_snprintf(conn->buffer, BUFFER_SIZE, " %u %u %" APR_SIZE_T_FMT " " MC_EOL,
flags, timeout, data_size);
vec[2].iov_base = conn->buffer;
vec[2].iov_len = klen;
vec[3].iov_base = data;
vec[3].iov_len = data_size;
vec[4].iov_base = MC_EOL;
vec[4].iov_len = MC_EOL_LEN;
rv = apr_socket_sendv(conn->sock, vec, 5, &written);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
if (strcmp(conn->buffer, MS_STORED MC_EOL) == 0) {
rv = APR_SUCCESS;
}
else if (strcmp(conn->buffer, MS_NOT_STORED MC_EOL) == 0) {
rv = APR_EEXIST;
}
else {
rv = APR_EGENERAL;
}
ms_release_conn(ms, conn);
return rv;
}
APU_DECLARE(apr_status_t)
apr_memcache_set(apr_memcache_t *mc,
const char *key,
char *data,
const apr_size_t data_size,
apr_uint32_t timeout,
apr_uint16_t flags)
{
return storage_cmd_write(mc,
MC_SET, MC_SET_LEN,
key,
data, data_size,
timeout, flags);
}
APU_DECLARE(apr_status_t)
apr_memcache_add(apr_memcache_t *mc,
const char *key,
char *data,
const apr_size_t data_size,
apr_uint32_t timeout,
apr_uint16_t flags)
{
return storage_cmd_write(mc,
MC_ADD, MC_ADD_LEN,
key,
data, data_size,
timeout, flags);
}
APU_DECLARE(apr_status_t)
apr_memcache_replace(apr_memcache_t *mc,
const char *key,
char *data,
const apr_size_t data_size,
apr_uint32_t timeout,
apr_uint16_t flags)
{
return storage_cmd_write(mc,
MC_REPLACE, MC_REPLACE_LEN,
key,
data, data_size,
timeout, flags);
}
APU_DECLARE(apr_status_t)
apr_memcache_getp(apr_memcache_t *mc,
apr_pool_t *p,
const char *key,
char **baton,
apr_size_t *new_length,
apr_uint16_t *flags_)
{
apr_status_t rv;
apr_memcache_server_t *ms;
apr_memcache_conn_t *conn;
apr_uint32_t hash;
apr_size_t written;
apr_size_t klen = strlen(key);
struct iovec vec[3];
hash = apr_memcache_hash(mc, key, klen);
ms = apr_memcache_find_server_hash(mc, hash);
if (ms == NULL)
return APR_NOTFOUND;
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
apr_memcache_disable_server(mc, ms);
return rv;
}
/* get <key>[ <key>[...]]\r\n */
vec[0].iov_base = MC_GET;
vec[0].iov_len = MC_GET_LEN;
vec[1].iov_base = (void*)key;
vec[1].iov_len = klen;
vec[2].iov_base = MC_EOL;
vec[2].iov_len = MC_EOL_LEN;
rv = apr_socket_sendv(conn->sock, vec, 3, &written);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
if (strncmp(MS_VALUE, conn->buffer, MS_VALUE_LEN) == 0) {
char *flags;
char *length;
char *last;
apr_size_t len = 0;
flags = apr_strtok(conn->buffer, " ", &last);
flags = apr_strtok(NULL, " ", &last);
flags = apr_strtok(NULL, " ", &last);
if (flags_) {
*flags_ = atoi(flags);
}
length = apr_strtok(NULL, " ", &last);
if (length) {
len = atoi(length);
}
if (len < 0) {
*new_length = 0;
*baton = NULL;
}
else {
apr_bucket_brigade *bbb;
apr_bucket *e;
/* eat the trailing \r\n */
rv = apr_brigade_partition(conn->bb, len+2, &e);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
bbb = apr_brigade_split(conn->bb, e);
rv = apr_brigade_pflatten(conn->bb, baton, &len, p);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
rv = apr_brigade_destroy(conn->bb);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
conn->bb = bbb;
*new_length = len - 2;
(*baton)[*new_length] = '\0';
}
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
if (strncmp(MS_END, conn->buffer, MS_END_LEN) != 0) {
rv = APR_EGENERAL;
}
}
else if (strncmp(MS_END, conn->buffer, MS_END_LEN) == 0) {
rv = APR_NOTFOUND;
}
else {
rv = APR_EGENERAL;
}
ms_release_conn(ms, conn);
return rv;
}
APU_DECLARE(apr_status_t)
apr_memcache_delete(apr_memcache_t *mc,
const char *key,
apr_uint32_t timeout)
{
apr_status_t rv;
apr_memcache_server_t *ms;
apr_memcache_conn_t *conn;
apr_uint32_t hash;
apr_size_t written;
struct iovec vec[3];
apr_size_t klen = strlen(key);
hash = apr_memcache_hash(mc, key, klen);
ms = apr_memcache_find_server_hash(mc, hash);
if (ms == NULL)
return APR_NOTFOUND;
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
apr_memcache_disable_server(mc, ms);
return rv;
}
/* delete <key> <time>\r\n */
vec[0].iov_base = MC_DELETE;
vec[0].iov_len = MC_DELETE_LEN;
vec[1].iov_base = (void*)key;
vec[1].iov_len = klen;
klen = apr_snprintf(conn->buffer, BUFFER_SIZE, " %u" MC_EOL, timeout);
vec[2].iov_base = conn->buffer;
vec[2].iov_len = klen;
rv = apr_socket_sendv(conn->sock, vec, 3, &written);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
if (strncmp(MS_DELETED, conn->buffer, MS_DELETED_LEN) == 0) {
rv = APR_SUCCESS;
}
else if (strncmp(MS_NOT_FOUND, conn->buffer, MS_NOT_FOUND_LEN) == 0) {
rv = APR_NOTFOUND;
}
else {
rv = APR_EGENERAL;
}
ms_release_conn(ms, conn);
return rv;
}
static apr_status_t num_cmd_write(apr_memcache_t *mc,
char *cmd,
const apr_uint32_t cmd_size,
const char *key,
const apr_int32_t inc,
apr_uint32_t *new_value)
{
apr_status_t rv;
apr_memcache_server_t *ms;
apr_memcache_conn_t *conn;
apr_uint32_t hash;
apr_size_t written;
struct iovec vec[3];
apr_size_t klen = strlen(key);
hash = apr_memcache_hash(mc, key, klen);
ms = apr_memcache_find_server_hash(mc, hash);
if (ms == NULL)
return APR_NOTFOUND;
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
apr_memcache_disable_server(mc, ms);
return rv;
}
/* <cmd> <key> <value>\r\n */
vec[0].iov_base = cmd;
vec[0].iov_len = cmd_size;
vec[1].iov_base = (void*)key;
vec[1].iov_len = klen;
klen = apr_snprintf(conn->buffer, BUFFER_SIZE, " %u" MC_EOL, inc);
vec[2].iov_base = conn->buffer;
vec[2].iov_len = klen;
rv = apr_socket_sendv(conn->sock, vec, 3, &written);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
apr_memcache_disable_server(mc, ms);
return rv;
}
if (strncmp(MS_ERROR, conn->buffer, MS_ERROR_LEN) == 0) {
rv = APR_EGENERAL;
}
else if (strncmp(MS_NOT_FOUND, conn->buffer, MS_NOT_FOUND_LEN) == 0) {
rv = APR_NOTFOUND;
}
else {
if (new_value) {
*new_value = atoi(conn->buffer);
}
rv = APR_SUCCESS;
}
ms_release_conn(ms, conn);
return rv;
}
APU_DECLARE(apr_status_t)
apr_memcache_incr(apr_memcache_t *mc,
const char *key,
apr_int32_t inc,
apr_uint32_t *new_value)
{
return num_cmd_write(mc,
MC_INCR,
MC_INCR_LEN,
key,
inc,
new_value);
}
APU_DECLARE(apr_status_t)
apr_memcache_decr(apr_memcache_t *mc,
const char *key,
apr_int32_t inc,
apr_uint32_t *new_value)
{
return num_cmd_write(mc,
MC_DECR,
MC_DECR_LEN,
key,
inc,
new_value);
}
APU_DECLARE(apr_status_t)
apr_memcache_version(apr_memcache_server_t *ms,
apr_pool_t *p,
char **baton)
{
apr_status_t rv;
apr_memcache_conn_t *conn;
apr_size_t written;
struct iovec vec[2];
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
return rv;
}
/* version\r\n */
vec[0].iov_base = MC_VERSION;
vec[0].iov_len = MC_VERSION_LEN;
vec[1].iov_base = MC_EOL;
vec[1].iov_len = MC_EOL_LEN;
rv = apr_socket_sendv(conn->sock, vec, 2, &written);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
return rv;
}
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
return rv;
}
if (strncmp(MS_VERSION, conn->buffer, MS_VERSION_LEN) == 0) {
*baton = apr_pstrmemdup(p, conn->buffer+MS_VERSION_LEN+1,
conn->blen - MS_VERSION_LEN - 2);
rv = APR_SUCCESS;
}
else {
rv = APR_EGENERAL;
}
ms_release_conn(ms, conn);
return rv;
}
apr_status_t mc_version_ping(apr_memcache_server_t *ms)
{
apr_status_t rv;
apr_size_t written;
struct iovec vec[2];
apr_memcache_conn_t *conn;
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
return rv;
}
/* version\r\n */
vec[0].iov_base = MC_VERSION;
vec[0].iov_len = MC_VERSION_LEN;
vec[1].iov_base = MC_EOL;
vec[1].iov_len = MC_EOL_LEN;
rv = apr_socket_sendv(conn->sock, vec, 2, &written);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
return rv;
}
rv = get_server_line(conn);
ms_release_conn(ms, conn);
return rv;
}
APU_DECLARE(void)
apr_memcache_add_multget_key(apr_pool_t *data_pool,
const char* key,
apr_hash_t **values)
{
apr_memcache_value_t* value;
apr_size_t klen = strlen(key);
/* create the value hash if need be */
if (!*values) {
*values = apr_hash_make(data_pool);
}
/* init key and add it to the value hash */
value = apr_pcalloc(data_pool, sizeof(apr_memcache_value_t));
value->status = APR_NOTFOUND;
value->key = apr_pstrdup(data_pool, key);
apr_hash_set(*values, value->key, klen, value);
}
static void mget_conn_result(int serverup,
int connup,
apr_status_t rv,
apr_memcache_t *mc,
apr_memcache_server_t *ms,
apr_memcache_conn_t *conn,
struct cache_server_query_t *server_query,
apr_hash_t *values,
apr_hash_t *server_queries)
{
apr_int32_t j;
apr_memcache_value_t* value;
apr_hash_set(server_queries, &ms, sizeof(ms), NULL);
if (connup) {
ms_release_conn(ms, conn);
} else {
ms_bad_conn(ms, conn);
if (!serverup) {
apr_memcache_disable_server(mc, ms);
}
}
for (j = 1; j < server_query->query_vec_count ; j+=2) {
if (server_query->query_vec[j].iov_base) {
value = apr_hash_get(values, server_query->query_vec[j].iov_base,
strlen(server_query->query_vec[j].iov_base));
if (value->status == APR_NOTFOUND) {
value->status = rv;
}
}
}
}
APU_DECLARE(apr_status_t)
apr_memcache_multgetp(apr_memcache_t *mc,
apr_pool_t *temp_pool,
apr_pool_t *data_pool,
apr_hash_t *values)
{
apr_status_t rv;
apr_memcache_server_t* ms;
apr_memcache_conn_t* conn;
apr_uint32_t hash;
apr_size_t written;
apr_size_t klen;
apr_memcache_value_t* value;
apr_hash_index_t* value_hash_index;
/* this is a little over aggresive, but beats multiple loops
* to figure out how long each vector needs to be per-server.
*/
apr_int32_t veclen = 2 + 2 * apr_hash_count(values) - 1; /* get <key>[<space><key>...]\r\n */
apr_int32_t i, j;
apr_int32_t queries_sent;
apr_int32_t queries_recvd;
apr_hash_t * server_queries = apr_hash_make(temp_pool);
struct cache_server_query_t* server_query;
apr_hash_index_t * query_hash_index;
apr_pollset_t* pollset;
const apr_pollfd_t* activefds;
apr_pollfd_t* pollfds;
/* build all the queries */
value_hash_index = apr_hash_first(temp_pool, values);
while (value_hash_index) {
void *v;
apr_hash_this(value_hash_index, NULL, NULL, &v);
value = v;
value_hash_index = apr_hash_next(value_hash_index);
klen = strlen(value->key);
hash = apr_memcache_hash(mc, value->key, klen);
ms = apr_memcache_find_server_hash(mc, hash);
if (ms == NULL) {
continue;
}
server_query = apr_hash_get(server_queries, &ms, sizeof(ms));
if (!server_query) {
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
apr_memcache_disable_server(mc, ms);
value->status = rv;
continue;
}
server_query = apr_pcalloc(temp_pool,sizeof(struct cache_server_query_t));
apr_hash_set(server_queries, &ms, sizeof(ms), server_query);
server_query->ms = ms;
server_query->conn = conn;
server_query->query_vec = apr_pcalloc(temp_pool, sizeof(struct iovec)*veclen);
/* set up the first key */
server_query->query_vec[0].iov_base = MC_GET;
server_query->query_vec[0].iov_len = MC_GET_LEN;
server_query->query_vec[1].iov_base = (void*)(value->key);
server_query->query_vec[1].iov_len = klen;
server_query->query_vec[2].iov_base = MC_EOL;
server_query->query_vec[2].iov_len = MC_EOL_LEN;
server_query->query_vec_count = 3;
}
else {
j = server_query->query_vec_count - 1;
server_query->query_vec[j].iov_base = MC_WS;
server_query->query_vec[j].iov_len = MC_WS_LEN;
j++;
server_query->query_vec[j].iov_base = (void*)(value->key);
server_query->query_vec[j].iov_len = klen;
j++;
server_query->query_vec[j].iov_base = MC_EOL;
server_query->query_vec[j].iov_len = MC_EOL_LEN;
j++;
server_query->query_vec_count = j;
}
}
/* create polling structures */
pollfds = apr_pcalloc(temp_pool, apr_hash_count(server_queries) * sizeof(apr_pollfd_t));
rv = apr_pollset_create(&pollset, apr_hash_count(server_queries), temp_pool, 0);
if (rv != APR_SUCCESS) {
query_hash_index = apr_hash_first(temp_pool, server_queries);
while (query_hash_index) {
void *v;
apr_hash_this(query_hash_index, NULL, NULL, &v);
server_query = v;
query_hash_index = apr_hash_next(query_hash_index);
mget_conn_result(TRUE, TRUE, rv, mc, server_query->ms, server_query->conn,
server_query, values, server_queries);
}
return rv;
}
/* send all the queries */
queries_sent = 0;
query_hash_index = apr_hash_first(temp_pool, server_queries);
while (query_hash_index) {
void *v;
apr_hash_this(query_hash_index, NULL, NULL, &v);
server_query = v;
query_hash_index = apr_hash_next(query_hash_index);
conn = server_query->conn;
ms = server_query->ms;
for (i = 0, rv = APR_SUCCESS; i < veclen && rv == APR_SUCCESS; i += APR_MAX_IOVEC_SIZE) {
rv = apr_socket_sendv(conn->sock, &(server_query->query_vec[i]),
veclen-i>APR_MAX_IOVEC_SIZE ? APR_MAX_IOVEC_SIZE : veclen-i , &written);
}
if (rv != APR_SUCCESS) {
mget_conn_result(FALSE, FALSE, rv, mc, ms, conn,
server_query, values, server_queries);
continue;
}
pollfds[queries_sent].desc_type = APR_POLL_SOCKET;
pollfds[queries_sent].reqevents = APR_POLLIN;
pollfds[queries_sent].p = temp_pool;
pollfds[queries_sent].desc.s = conn->sock;
pollfds[queries_sent].client_data = (void *)server_query;
apr_pollset_add (pollset, &pollfds[queries_sent]);
queries_sent++;
}
while (queries_sent) {
rv = apr_pollset_poll(pollset, MULT_GET_TIMEOUT, &queries_recvd, &activefds);
if (rv != APR_SUCCESS) {
/* timeout */
queries_sent = 0;
continue;
}
for (i = 0; i < queries_recvd; i++) {
server_query = activefds[i].client_data;
conn = server_query->conn;
ms = server_query->ms;
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
apr_pollset_remove (pollset, &activefds[i]);
mget_conn_result(FALSE, FALSE, rv, mc, ms, conn,
server_query, values, server_queries);
queries_sent--;
continue;
}
if (strncmp(MS_VALUE, conn->buffer, MS_VALUE_LEN) == 0) {
char *key;
char *flags;
char *length;
char *last;
char *data;
apr_size_t len = 0;
key = apr_strtok(conn->buffer, " ", &last); /* just the VALUE, ignore */
key = apr_strtok(NULL, " ", &last);
flags = apr_strtok(NULL, " ", &last);
length = apr_strtok(NULL, " ", &last);
if (length) {
len = atoi(length);
}
value = apr_hash_get(values, key, strlen(key));
if (value) {
if (len >= 0) {
apr_bucket_brigade *bbb;
apr_bucket *e;
/* eat the trailing \r\n */
rv = apr_brigade_partition(conn->bb, len+2, &e);
if (rv != APR_SUCCESS) {
apr_pollset_remove (pollset, &activefds[i]);
mget_conn_result(FALSE, FALSE, rv, mc, ms, conn,
server_query, values, server_queries);
queries_sent--;
continue;
}
bbb = apr_brigade_split(conn->bb, e);
rv = apr_brigade_pflatten(conn->bb, &data, &len, data_pool);
if (rv != APR_SUCCESS) {
apr_pollset_remove (pollset, &activefds[i]);
mget_conn_result(FALSE, FALSE, rv, mc, ms, conn,
server_query, values, server_queries);
queries_sent--;
continue;
}
rv = apr_brigade_destroy(conn->bb);
if (rv != APR_SUCCESS) {
apr_pollset_remove (pollset, &activefds[i]);
mget_conn_result(FALSE, FALSE, rv, mc, ms, conn,
server_query, values, server_queries);
queries_sent--;
continue;
}
conn->bb = bbb;
value->len = len - 2;
data[value->len] = '\0';
value->data = data;
}
value->status = rv;
value->flags = atoi(flags);
/* stay on the server */
i--;
}
else {
/* TODO: Server Sent back a key I didn't ask for or my
* hash is corrupt */
}
}
else if (strncmp(MS_END, conn->buffer, MS_END_LEN) == 0) {
/* this connection is done */
apr_pollset_remove (pollset, &activefds[i]);
ms_release_conn(ms, conn);
apr_hash_set(server_queries, &ms, sizeof(ms), NULL);
queries_sent--;
}
else {
/* unknown reply? */
rv = APR_EGENERAL;
}
} /* /for */
} /* /while */
query_hash_index = apr_hash_first(temp_pool, server_queries);
while (query_hash_index) {
void *v;
apr_hash_this(query_hash_index, NULL, NULL, &v);
server_query = v;
query_hash_index = apr_hash_next(query_hash_index);
conn = server_query->conn;
ms = server_query->ms;
mget_conn_result(TRUE, (rv == APR_SUCCESS), rv, mc, ms, conn,
server_query, values, server_queries);
continue;
}
apr_pollset_destroy(pollset);
apr_pool_clear(temp_pool);
return APR_SUCCESS;
}
/**
* Define all of the strings for stats
*/
#define STAT_pid MS_STAT " pid "
#define STAT_pid_LEN (sizeof(STAT_pid)-1)
#define STAT_uptime MS_STAT " uptime "
#define STAT_uptime_LEN (sizeof(STAT_uptime)-1)
#define STAT_time MS_STAT " time "
#define STAT_time_LEN (sizeof(STAT_time)-1)
#define STAT_version MS_STAT " version "
#define STAT_version_LEN (sizeof(STAT_version)-1)
#define STAT_pointer_size MS_STAT " pointer_size "
#define STAT_pointer_size_LEN (sizeof(STAT_pointer_size)-1)
#define STAT_rusage_user MS_STAT " rusage_user "
#define STAT_rusage_user_LEN (sizeof(STAT_rusage_user)-1)
#define STAT_rusage_system MS_STAT " rusage_system "
#define STAT_rusage_system_LEN (sizeof(STAT_rusage_system)-1)
#define STAT_curr_items MS_STAT " curr_items "
#define STAT_curr_items_LEN (sizeof(STAT_curr_items)-1)
#define STAT_total_items MS_STAT " total_items "
#define STAT_total_items_LEN (sizeof(STAT_total_items)-1)
#define STAT_bytes MS_STAT " bytes "
#define STAT_bytes_LEN (sizeof(STAT_bytes)-1)
#define STAT_curr_connections MS_STAT " curr_connections "
#define STAT_curr_connections_LEN (sizeof(STAT_curr_connections)-1)
#define STAT_total_connections MS_STAT " total_connections "
#define STAT_total_connections_LEN (sizeof(STAT_total_connections)-1)
#define STAT_connection_structures MS_STAT " connection_structures "
#define STAT_connection_structures_LEN (sizeof(STAT_connection_structures)-1)
#define STAT_cmd_get MS_STAT " cmd_get "
#define STAT_cmd_get_LEN (sizeof(STAT_cmd_get)-1)
#define STAT_cmd_set MS_STAT " cmd_set "
#define STAT_cmd_set_LEN (sizeof(STAT_cmd_set)-1)
#define STAT_get_hits MS_STAT " get_hits "
#define STAT_get_hits_LEN (sizeof(STAT_get_hits)-1)
#define STAT_get_misses MS_STAT " get_misses "
#define STAT_get_misses_LEN (sizeof(STAT_get_misses)-1)
#define STAT_evictions MS_STAT " evictions "
#define STAT_evictions_LEN (sizeof(STAT_evictions)-1)
#define STAT_bytes_read MS_STAT " bytes_read "
#define STAT_bytes_read_LEN (sizeof(STAT_bytes_read)-1)
#define STAT_bytes_written MS_STAT " bytes_written "
#define STAT_bytes_written_LEN (sizeof(STAT_bytes_written)-1)
#define STAT_limit_maxbytes MS_STAT " limit_maxbytes "
#define STAT_limit_maxbytes_LEN (sizeof(STAT_limit_maxbytes)-1)
#define STAT_threads MS_STAT " threads "
#define STAT_threads_LEN (sizeof(STAT_threads)-1)
static const char *stat_read_string(apr_pool_t *p, char *buf, apr_size_t len)
{
/* remove trailing \r\n and null char */
return apr_pstrmemdup(p, buf, len-2);
}
static apr_uint32_t stat_read_uint32(apr_pool_t *p, char *buf, apr_size_t len)
{
buf[len-2] = '\0';
return atoi(buf);
}
static apr_uint64_t stat_read_uint64(apr_pool_t *p, char *buf, apr_size_t len)
{
buf[len-2] = '\0';
return apr_atoi64(buf);
}
static apr_time_t stat_read_time(apr_pool_t *p, char *buf, apr_size_t len)
{
buf[len-2] = '\0';
return apr_time_from_sec(atoi(buf));
}
static apr_time_t stat_read_rtime(apr_pool_t *p, char *buf, apr_size_t len)
{
char *tok;
char *secs;
char *usecs;
const char *sep = ":.";
buf[len-2] = '\0';
secs = apr_strtok(buf, sep, &tok);
usecs = apr_strtok(NULL, sep, &tok);
if (secs && usecs) {
return apr_time_make(atoi(secs), atoi(usecs));
}
else {
return apr_time_make(0, 0);
}
}
/**
* I got tired of Typing. Meh.
*
* TODO: Convert it to static tables to make it cooler.
*/
#define mc_stat_cmp(name) \
strncmp(STAT_ ## name, conn->buffer, STAT_ ## name ## _LEN) == 0
#define mc_stat_str(name) \
stat_read_string(p, conn->buffer + name, \
conn->blen - name)
#define mc_stat_uint32(name) \
stat_read_uint32(p, conn->buffer + name, \
conn->blen - name)
#define mc_stat_uint64(name) \
stat_read_uint64(p, conn->buffer + name, \
conn->blen - name)
#define mc_stat_time(name) \
stat_read_time(p, conn->buffer + name, \
conn->blen - name)
#define mc_stat_rtime(name) \
stat_read_rtime(p, conn->buffer + name, \
conn->blen - name)
#define mc_do_stat(name, type) \
if (mc_stat_cmp(name)) { \
stats-> name = mc_stat_ ## type ((STAT_ ## name ## _LEN)); \
}
static void update_stats(apr_pool_t *p, apr_memcache_conn_t *conn,
apr_memcache_stats_t *stats)
{
mc_do_stat(version, str)
else mc_do_stat(pid, uint32)
else mc_do_stat(uptime, uint32)
else mc_do_stat(pointer_size, uint32)
else mc_do_stat(time, time)
else mc_do_stat(rusage_user, rtime)
else mc_do_stat(rusage_system, rtime)
else mc_do_stat(curr_items, uint32)
else mc_do_stat(total_items, uint32)
else mc_do_stat(bytes, uint64)
else mc_do_stat(curr_connections, uint32)
else mc_do_stat(total_connections, uint32)
else mc_do_stat(connection_structures, uint32)
else mc_do_stat(cmd_get, uint32)
else mc_do_stat(cmd_set, uint32)
else mc_do_stat(get_hits, uint32)
else mc_do_stat(get_misses, uint32)
else mc_do_stat(evictions, uint64)
else mc_do_stat(bytes_read, uint64)
else mc_do_stat(bytes_written, uint64)
else mc_do_stat(limit_maxbytes, uint32)
else mc_do_stat(threads, uint32)
}
APU_DECLARE(apr_status_t)
apr_memcache_stats(apr_memcache_server_t *ms,
apr_pool_t *p,
apr_memcache_stats_t **stats)
{
apr_memcache_stats_t *ret;
apr_status_t rv;
apr_memcache_conn_t *conn;
apr_size_t written;
struct iovec vec[2];
rv = ms_find_conn(ms, &conn);
if (rv != APR_SUCCESS) {
return rv;
}
/* version\r\n */
vec[0].iov_base = MC_STATS;
vec[0].iov_len = MC_STATS_LEN;
vec[1].iov_base = MC_EOL;
vec[1].iov_len = MC_EOL_LEN;
rv = apr_socket_sendv(conn->sock, vec, 2, &written);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
return rv;
}
ret = apr_pcalloc(p, sizeof(apr_memcache_stats_t));
do {
rv = get_server_line(conn);
if (rv != APR_SUCCESS) {
ms_bad_conn(ms, conn);
return rv;
}
if (strncmp(MS_END, conn->buffer, MS_END_LEN) == 0) {
rv = APR_SUCCESS;
break;
}
else if (strncmp(MS_STAT, conn->buffer, MS_STAT_LEN) == 0) {
update_stats(p, conn, ret);
continue;
}
else {
rv = APR_EGENERAL;
break;
}
} while(1);
ms_release_conn(ms, conn);
if (stats) {
*stats = ret;
}
return rv;
}