Don't expect variable argument list to end with NULL
[apr-util.git] / memcache / apr_memcache.c
bloba0271c0e2608e7bd6d5bac12baed195543c3ba0c
1 /* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
2 * applicable.
4 * Copyright 2004 Paul Querna
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 #include "apr_memcache.h"
20 #include "apr_version.h"
21 #include <stdlib.h>
23 #define BUFFER_SIZE 512
24 struct apr_memcache_conn_t
26 char *buffer;
27 apr_size_t blen;
28 apr_pool_t *p;
29 apr_socket_t *sock;
30 apr_bucket_alloc_t *balloc;
31 apr_bucket_brigade *bb;
32 apr_bucket_brigade *tb;
33 apr_memcache_server_t *ms;
34 };
36 /* Strings for Client Commands */
38 #define MC_EOL "\r\n"
39 #define MC_EOL_LEN (sizeof(MC_EOL)-1)
41 #define MC_GET "get "
42 #define MC_GET_LEN (sizeof(MC_GET)-1)
44 #define MC_SET "set "
45 #define MC_SET_LEN (sizeof(MC_SET)-1)
47 #define MC_ADD "add "
48 #define MC_ADD_LEN (sizeof(MC_ADD)-1)
50 #define MC_REPLACE "replace "
51 #define MC_REPLACE_LEN (sizeof(MC_REPLACE)-1)
53 #define MC_DELETE "delete "
54 #define MC_DELETE_LEN (sizeof(MC_DELETE)-1)
56 #define MC_INCR "incr "
57 #define MC_INCR_LEN (sizeof(MC_INCR)-1)
59 #define MC_DECR "decr "
60 #define MC_DECR_LEN (sizeof(MC_DECR)-1)
62 #define MC_VERSION "version"
63 #define MC_VERSION_LEN (sizeof(MC_VERSION)-1)
65 #define MC_STATS "stats"
66 #define MC_STATS_LEN (sizeof(MC_STATS)-1)
68 /* Strings for Server Replies */
70 #define MS_STORED "STORED"
71 #define MS_STORED_LEN (sizeof(MS_STORED)-1)
73 #define MS_NOT_STORED "NOT_STORED"
74 #define MS_NOT_STORED_LEN (sizeof(MS_NOT_STORED)-1)
76 #define MS_DELETED "DELETED"
77 #define MS_DELETED_LEN (sizeof(MS_DELETED)-1)
79 #define MS_NOT_FOUND "NOT_FOUND"
80 #define MS_NOT_FOUND_LEN (sizeof(MS_NOT_FOUND)-1)
82 #define MS_VALUE "VALUE"
83 #define MS_VALUE_LEN (sizeof(MS_VALUE)-1)
85 #define MS_ERROR "ERROR"
86 #define MS_ERROR_LEN (sizeof(MS_ERROR)-1)
88 #define MS_VERSION "VERSION"
89 #define MS_VERSION_LEN (sizeof(MS_VERSION)-1)
91 #define MS_STAT "STAT"
92 #define MS_STAT_LEN (sizeof(MS_STAT)-1)
94 #define MS_END "END"
95 #define MS_END_LEN (sizeof(MS_END)-1)
98 static apr_status_t make_server_dead(apr_memcache_t *mc, apr_memcache_server_t *ms)
100 #if APR_HAS_THREADS
101 apr_thread_mutex_lock(ms->lock);
102 #endif
103 ms->status = APR_MC_SERVER_DEAD;
104 ms->btime = apr_time_now();
105 #if APR_HAS_THREADS
106 apr_thread_mutex_unlock(ms->lock);
107 #endif
108 return APR_SUCCESS;
111 static apr_status_t make_server_live(apr_memcache_t *mc, apr_memcache_server_t *ms)
113 ms->status = APR_MC_SERVER_LIVE;
114 return APR_SUCCESS;
118 APR_DECLARE(apr_status_t) apr_memcache_add_server(apr_memcache_t *mc, apr_memcache_server_t *ms)
120 apr_status_t rv = APR_SUCCESS;
122 if(mc->ntotal >= mc->nalloc) {
123 return APR_ENOMEM;
126 mc->live_servers[mc->ntotal] = ms;
127 mc->ntotal++;
128 make_server_live(mc, ms);
129 return rv;
132 static apr_status_t mc_version_ping(apr_memcache_server_t *ms);
134 APR_DECLARE(apr_memcache_server_t *)
135 apr_memcache_find_server_hash(apr_memcache_t *mc, const apr_uint32_t hash)
137 apr_memcache_server_t *ms = NULL;
138 apr_uint32_t h = hash;
139 apr_uint32_t i = 0;
140 apr_time_t curtime = 0;
142 if(mc->ntotal == 0) {
143 return NULL;
146 do {
147 ms = mc->live_servers[h % mc->ntotal];
148 if(ms->status == APR_MC_SERVER_LIVE) {
149 break;
151 else {
152 if (curtime == 0) {
153 curtime = apr_time_now();
155 #if APR_HAS_THREADS
156 apr_thread_mutex_lock(ms->lock);
157 #endif
158 /* Try the the dead server, every 5 seconds */
159 if (curtime - ms->btime > apr_time_from_sec(5)) {
160 if (mc_version_ping(ms) == APR_SUCCESS) {
161 ms->btime = curtime;
162 make_server_live(mc, ms);
163 #if APR_HAS_THREADS
164 apr_thread_mutex_unlock(ms->lock);
165 #endif
166 break;
169 #if APR_HAS_THREADS
170 apr_thread_mutex_unlock(ms->lock);
171 #endif
173 h++;
174 i++;
175 } while(i < mc->ntotal);
177 if (i == mc->ntotal) {
178 ms = NULL;
181 return ms;
184 APR_DECLARE(apr_memcache_server_t *) apr_memcache_find_server(apr_memcache_t *mc, const char *host, apr_port_t port)
186 int i;
188 for (i = 0; i < mc->ntotal; i++) {
189 if (strcmp(mc->live_servers[i]->host, host) == 0
190 && mc->live_servers[i]->port == port) {
192 return mc->live_servers[i];
196 return NULL;
199 static apr_status_t ms_find_conn(apr_memcache_server_t *ms, apr_memcache_conn_t **conn)
201 #if APR_HAS_THREADS
202 return apr_reslist_acquire(ms->conns, (void **)conn);
203 #else
204 *conn = ms->conn;
205 return APR_SUCCESS;
206 #endif
209 static apr_status_t ms_bad_conn(apr_memcache_server_t *ms, apr_memcache_conn_t *conn)
211 #if APR_HAS_THREADS
212 return apr_reslist_invalidate(ms->conns, conn);
213 #else
214 return APR_SUCCESS;
215 #endif
218 static apr_status_t ms_release_conn(apr_memcache_server_t *ms, apr_memcache_conn_t *conn)
220 #if APR_HAS_THREADS
221 return apr_reslist_release(ms->conns, conn);
222 #else
223 return APR_SUCCESS;
224 #endif
227 APR_DECLARE(apr_status_t) apr_memcache_enable_server(apr_memcache_t *mc, apr_memcache_server_t *ms)
229 apr_status_t rv = APR_SUCCESS;
231 if (ms->status == APR_MC_SERVER_LIVE) {
232 return rv;
235 rv = make_server_live(mc, ms);
236 return rv;
239 APR_DECLARE(apr_status_t) apr_memcache_disable_server(apr_memcache_t *mc, apr_memcache_server_t *ms)
241 return make_server_dead(mc, ms);
244 static apr_status_t conn_connect(apr_memcache_conn_t *conn)
246 apr_status_t rv = APR_SUCCESS;
247 apr_sockaddr_t *sa;
249 rv = apr_sockaddr_info_get(&sa, conn->ms->host, APR_INET, conn->ms->port, 0, conn->p);
250 if (rv != APR_SUCCESS) {
251 return rv;
254 rv = apr_socket_timeout_set(conn->sock, 1 * APR_USEC_PER_SEC);
255 if (rv != APR_SUCCESS) {
256 return rv;
259 rv = apr_socket_connect(conn->sock, sa);
260 if (rv != APR_SUCCESS) {
261 return rv;
264 return rv;
268 static apr_status_t
269 mc_conn_construct(void **conn_, void *params, apr_pool_t *pool)
271 apr_status_t rv = APR_SUCCESS;
272 apr_memcache_conn_t *conn;
273 apr_bucket *e;
274 apr_pool_t *np;
275 apr_memcache_server_t *ms = params;
277 rv = apr_pool_create(&np, pool);
278 if (rv != APR_SUCCESS) {
279 return rv;
282 conn = apr_palloc(np, sizeof( apr_memcache_conn_t ));
284 conn->p = np;
286 rv = apr_socket_create(&conn->sock, APR_INET, SOCK_STREAM, 0, np);
288 if (rv != APR_SUCCESS) {
289 return rv;
292 conn->balloc = apr_bucket_alloc_create(conn->p);
293 conn->bb = apr_brigade_create(conn->p, conn->balloc);
294 conn->tb = apr_brigade_create(conn->p, conn->balloc);
295 conn->buffer = apr_palloc(conn->p, BUFFER_SIZE);
296 conn->blen = 0;
297 conn->ms = ms;
299 e = apr_bucket_socket_create(conn->sock, conn->balloc);
300 APR_BRIGADE_INSERT_TAIL(conn->bb, e);
302 rv = conn_connect(conn);
303 *conn_ = conn;
305 return rv;
308 static apr_status_t
309 mc_conn_destruct(void *conn_, void *params, apr_pool_t *pool)
311 /* Currently a NOOP */
312 return APR_SUCCESS;
315 APR_DECLARE(apr_status_t) apr_memcache_server_create(apr_pool_t *p,
316 const char *host, apr_port_t port,
317 apr_uint32_t min, apr_uint32_t smax,
318 apr_uint32_t max, apr_uint32_t ttl,
319 apr_memcache_server_t **ms)
321 apr_status_t rv = APR_SUCCESS;
322 apr_memcache_server_t *server;
323 apr_pool_t *np;
325 rv = apr_pool_create(&np, p);
327 server = apr_palloc(np, sizeof(apr_memcache_server_t));
329 server->p = np;
330 server->host = apr_pstrdup(np, host);
331 server->port = port;
332 server->status = APR_MC_SERVER_DEAD;
333 #if APR_HAS_THREADS
334 rv = apr_thread_mutex_create(&server->lock, APR_THREAD_MUTEX_DEFAULT, np);
335 if (rv != APR_SUCCESS) {
336 return rv;
339 rv = apr_reslist_create(&server->conns,
340 min, /* hard minimum */
341 smax, /* soft maximum */
342 max, /* hard maximum */
343 ttl, /* Time to live */
344 mc_conn_construct, /* Make a New Connection */
345 mc_conn_destruct, /* Kill Old Connection */
346 server, np);
347 #else
348 rv = mc_conn_construct((void**)&(server->conn), server, np);
349 #endif
351 if (rv != APR_SUCCESS) {
352 return rv;
355 *ms = server;
357 return rv;
360 APR_DECLARE(apr_status_t) apr_memcache_create(apr_pool_t *p,
361 apr_uint16_t max_servers, apr_uint32_t flags,
362 apr_memcache_t **memcache)
364 apr_status_t rv = APR_SUCCESS;
365 apr_memcache_t *mc;
367 mc = apr_palloc(p, sizeof(apr_memcache_t));
368 mc->p = p;
369 mc->nalloc = max_servers;
370 mc->ntotal = 0;
371 mc->live_servers = apr_palloc(p, mc->nalloc * sizeof(struct apr_memcache_server_t *));
372 *memcache = mc;
373 return rv;
377 /* The crc32 functions and data was originally written by Spencer
378 * Garrett <srg@quick.com> and was gleaned from the PostgreSQL source
379 * tree via the files contrib/ltree/crc32.[ch] and from FreeBSD at
380 * src/usr.bin/cksum/crc32.c.
383 static const apr_uint32_t crc32tab[256] = {
384 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
385 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
386 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
387 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
388 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
389 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
390 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
391 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
392 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
393 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
394 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
395 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
396 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
397 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
398 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
399 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
400 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
401 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
402 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
403 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
404 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
405 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
406 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
407 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
408 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
409 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
410 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
411 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
412 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
413 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
414 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
415 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
416 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
417 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
418 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
419 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
420 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
421 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
422 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
423 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
424 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
425 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
426 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
427 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
428 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
429 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
430 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
431 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
432 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
433 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
434 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
435 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
436 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
437 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
438 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
439 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
440 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
441 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
442 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
443 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
444 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
445 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
446 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
447 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
450 APR_DECLARE(apr_uint32_t) apr_memcache_hash(const char *data, const apr_size_t data_len)
452 apr_uint32_t i;
453 apr_uint32_t crc;
454 crc = ~0;
456 for (i = 0; i < data_len; i++)
457 crc = (crc >> 8) ^ crc32tab[(crc ^ (data[i])) & 0xff];
459 return ((~crc >> 16) & 0x7fff);
462 static apr_status_t get_server_line(apr_memcache_conn_t *conn)
464 apr_size_t bsize = BUFFER_SIZE;
465 apr_status_t rv = APR_SUCCESS;
467 rv = apr_brigade_split_line(conn->tb, conn->bb, APR_BLOCK_READ, BUFFER_SIZE);
469 if (rv != APR_SUCCESS) {
470 return rv;
473 rv = apr_brigade_flatten(conn->tb, conn->buffer, &bsize);
475 if (rv != APR_SUCCESS) {
476 return rv;
479 conn->blen = bsize;
480 conn->buffer[bsize] = '\0';
482 return apr_brigade_cleanup(conn->tb);
485 static apr_status_t storage_cmd_write(apr_memcache_t *mc,
486 char *cmd,
487 const apr_size_t cmd_size,
488 const char *key,
489 char *data,
490 const apr_size_t data_size,
491 apr_uint32_t timeout,
492 apr_uint16_t flags)
494 apr_uint32_t hash;
495 apr_memcache_server_t *ms;
496 apr_memcache_conn_t *conn;
497 apr_status_t rv;
498 apr_size_t written;
499 struct iovec vec[5];
500 int klen;
502 apr_size_t key_size = strlen(key);
504 hash = apr_memcache_hash(key, key_size);
506 ms = apr_memcache_find_server_hash(mc, hash);
508 if (ms == NULL)
509 return APR_NOTFOUND;
511 rv = ms_find_conn(ms, &conn);
513 if (rv != APR_SUCCESS) {
514 apr_memcache_disable_server(mc, ms);
515 return rv;
518 /* <command name> <key> <flags> <exptime> <bytes>\r\n<data>\r\n */
520 vec[0].iov_base = cmd;
521 vec[0].iov_len = cmd_size;
523 vec[1].iov_base = (void*)key;
524 vec[1].iov_len = key_size;
526 klen = apr_snprintf(conn->buffer, BUFFER_SIZE, " %u %u %" APR_SIZE_T_FMT " " MC_EOL,
527 flags, timeout, data_size);
529 vec[2].iov_base = conn->buffer;
530 vec[2].iov_len = klen;
532 vec[3].iov_base = data;
533 vec[3].iov_len = data_size;
535 vec[4].iov_base = MC_EOL;
536 vec[4].iov_len = MC_EOL_LEN;
538 rv = apr_socket_sendv(conn->sock, vec, 5, &written);
540 if (rv != APR_SUCCESS) {
541 ms_bad_conn(ms, conn);
542 apr_memcache_disable_server(mc, ms);
543 return rv;
546 rv = get_server_line(conn);
548 if (rv != APR_SUCCESS) {
549 ms_bad_conn(ms, conn);
550 apr_memcache_disable_server(mc, ms);
551 return rv;
554 if (strcmp(conn->buffer, MS_STORED MC_EOL) == 0) {
555 rv = APR_SUCCESS;
557 else if (strcmp(conn->buffer, MS_NOT_STORED MC_EOL) == 0) {
558 rv = APR_EEXIST;
560 else {
561 rv = APR_EGENERAL;
564 ms_release_conn(ms, conn);
566 return rv;
569 APR_DECLARE(apr_status_t)
570 apr_memcache_set(apr_memcache_t *mc,
571 const char *key,
572 char *data,
573 const apr_size_t data_size,
574 apr_uint32_t timeout,
575 apr_uint16_t flags)
577 return storage_cmd_write(mc,
578 MC_SET, MC_SET_LEN,
579 key,
580 data, data_size,
581 timeout, flags);
584 APR_DECLARE(apr_status_t)
585 apr_memcache_add(apr_memcache_t *mc,
586 const char *key,
587 char *data,
588 const apr_size_t data_size,
589 apr_uint32_t timeout,
590 apr_uint16_t flags)
592 return storage_cmd_write(mc,
593 MC_ADD, MC_ADD_LEN,
594 key,
595 data, data_size,
596 timeout, flags);
599 APR_DECLARE(apr_status_t)
600 apr_memcache_replace(apr_memcache_t *mc,
601 const char *key,
602 char *data,
603 const apr_size_t data_size,
604 apr_uint32_t timeout,
605 apr_uint16_t flags)
607 return storage_cmd_write(mc,
608 MC_REPLACE, MC_REPLACE_LEN,
609 key,
610 data, data_size,
611 timeout, flags);
615 APR_DECLARE(apr_status_t)
616 apr_memcache_getp(apr_memcache_t *mc,
617 apr_pool_t *p,
618 const char *key,
619 char **baton,
620 apr_size_t *new_length,
621 apr_uint16_t *flags_)
623 apr_status_t rv;
624 apr_memcache_server_t *ms;
625 apr_memcache_conn_t *conn;
626 apr_uint32_t hash;
627 apr_size_t written;
628 int klen = strlen(key);
629 struct iovec vec[3];
631 hash = apr_memcache_hash(key, klen);
632 ms = apr_memcache_find_server_hash(mc, hash);
633 if (ms == NULL)
634 return APR_NOTFOUND;
636 rv = ms_find_conn(ms, &conn);
638 if (rv != APR_SUCCESS) {
639 apr_memcache_disable_server(mc, ms);
640 return rv;
643 /* get <key>[ <key>[...]]\r\n */
644 vec[0].iov_base = MC_GET;
645 vec[0].iov_len = MC_GET_LEN;
647 vec[1].iov_base = (void*)key;
648 vec[1].iov_len = klen;
650 vec[2].iov_base = MC_EOL;
651 vec[2].iov_len = MC_EOL_LEN;
653 rv = apr_socket_sendv(conn->sock, vec, 3, &written);
655 if (rv != APR_SUCCESS) {
656 ms_bad_conn(ms, conn);
657 apr_memcache_disable_server(mc, ms);
658 return rv;
661 rv = get_server_line(conn);
662 if (rv != APR_SUCCESS) {
663 ms_bad_conn(ms, conn);
664 apr_memcache_disable_server(mc, ms);
665 return rv;
668 if (strncmp(MS_VALUE, conn->buffer, MS_VALUE_LEN) == 0) {
669 char *flags;
670 char *length;
671 char *start;
672 char *last;
673 apr_size_t len;
675 start = conn->buffer;
676 flags = apr_strtok(conn->buffer," ",&last);
677 flags = apr_strtok(NULL," ",&last);
678 flags = apr_strtok(NULL," ",&last);
680 if (flags_)
681 *flags_ = atoi(flags);
683 length = apr_strtok(NULL," ",&last);
684 len = atoi(length);
685 if (len < 0) {
686 *new_length = 0;
687 *baton = NULL;
689 else {
690 apr_bucket_brigade *bbb;
691 apr_bucket *e;
693 /* eat the trailing \r\n */
694 rv = apr_brigade_partition(conn->bb, len+2, &e);
696 if (rv != APR_SUCCESS) {
697 ms_bad_conn(ms, conn);
698 apr_memcache_disable_server(mc, ms);
699 return rv;
702 bbb = apr_brigade_split(conn->bb, e);
704 rv = apr_brigade_pflatten(conn->bb, baton, &len, p);
706 if (rv != APR_SUCCESS) {
707 ms_bad_conn(ms, conn);
708 apr_memcache_disable_server(mc, ms);
709 return rv;
712 rv = apr_brigade_destroy(conn->bb);
713 if (rv != APR_SUCCESS) {
714 ms_bad_conn(ms, conn);
715 apr_memcache_disable_server(mc, ms);
716 return rv;
719 conn->bb = bbb;
721 *new_length = len - 2;
722 (*baton)[*new_length] = '\0';
725 rv = get_server_line(conn);
726 if (rv != APR_SUCCESS) {
727 ms_bad_conn(ms, conn);
728 apr_memcache_disable_server(mc, ms);
729 return rv;
732 if (strncmp(MS_END, conn->buffer, MS_END_LEN) != 0) {
733 rv = APR_EGENERAL;
736 else if (strncmp(MS_END, conn->buffer, MS_END_LEN) == 0) {
737 rv = APR_NOTFOUND;
739 else {
740 rv = APR_EGENERAL;
743 ms_release_conn(ms, conn);
745 return rv;
748 APR_DECLARE(apr_status_t)
749 apr_memcache_delete(apr_memcache_t *mc,
750 const char *key,
751 apr_uint32_t timeout)
753 apr_status_t rv;
754 apr_memcache_server_t *ms;
755 apr_memcache_conn_t *conn;
756 apr_uint32_t hash;
757 apr_size_t written;
758 struct iovec vec[3];
759 int klen = strlen(key);
761 hash = apr_memcache_hash(key, klen);
762 ms = apr_memcache_find_server_hash(mc, hash);
763 if (ms == NULL)
764 return APR_NOTFOUND;
766 rv = ms_find_conn(ms, &conn);
768 if (rv != APR_SUCCESS) {
769 apr_memcache_disable_server(mc, ms);
770 return rv;
773 /* delete <key> <time>\r\n */
774 vec[0].iov_base = MC_DELETE;
775 vec[0].iov_len = MC_DELETE_LEN;
777 vec[1].iov_base = (void*)key;
778 vec[1].iov_len = klen;
780 klen = apr_snprintf(conn->buffer, BUFFER_SIZE, " %u" MC_EOL, timeout);
782 vec[2].iov_base = conn->buffer;
783 vec[2].iov_len = klen;
785 rv = apr_socket_sendv(conn->sock, vec, 3, &written);
787 if (rv != APR_SUCCESS) {
788 ms_bad_conn(ms, conn);
789 apr_memcache_disable_server(mc, ms);
790 return rv;
793 rv = get_server_line(conn);
794 if (rv != APR_SUCCESS) {
795 ms_bad_conn(ms, conn);
796 apr_memcache_disable_server(mc, ms);
797 return rv;
800 if (strncmp(MS_DELETED, conn->buffer, MS_DELETED_LEN) == 0) {
801 rv = APR_SUCCESS;
803 else if (strncmp(MS_NOT_FOUND, conn->buffer, MS_NOT_FOUND_LEN) == 0) {
804 rv = APR_NOTFOUND;
806 else {
807 rv = APR_EGENERAL;
810 ms_release_conn(ms, conn);
812 return rv;
815 static apr_status_t num_cmd_write(apr_memcache_t *mc,
816 char *cmd,
817 const apr_uint32_t cmd_size,
818 const char *key,
819 const apr_int32_t inc,
820 apr_uint32_t *new_value)
822 apr_status_t rv;
823 apr_memcache_server_t *ms;
824 apr_memcache_conn_t *conn;
825 apr_uint32_t hash;
826 apr_size_t written;
827 struct iovec vec[3];
828 int klen = strlen(key);
830 hash = apr_memcache_hash(key, klen);
831 ms = apr_memcache_find_server_hash(mc, hash);
832 if (ms == NULL)
833 return APR_NOTFOUND;
835 rv = ms_find_conn(ms, &conn);
837 if (rv != APR_SUCCESS) {
838 apr_memcache_disable_server(mc, ms);
839 return rv;
842 /* <cmd> <key> <value>\r\n */
843 vec[0].iov_base = cmd;
844 vec[0].iov_len = cmd_size;
846 vec[1].iov_base = (void*)key;
847 vec[1].iov_len = klen;
849 klen = apr_snprintf(conn->buffer, BUFFER_SIZE, " %u" MC_EOL, inc);
851 vec[2].iov_base = conn->buffer;
852 vec[2].iov_len = klen;
854 rv = apr_socket_sendv(conn->sock, vec, 3, &written);
856 if (rv != APR_SUCCESS) {
857 ms_bad_conn(ms, conn);
858 apr_memcache_disable_server(mc, ms);
859 return rv;
862 rv = get_server_line(conn);
863 if (rv != APR_SUCCESS) {
864 ms_bad_conn(ms, conn);
865 apr_memcache_disable_server(mc, ms);
866 return rv;
869 if (strncmp(MS_ERROR, conn->buffer, MS_ERROR_LEN) == 0) {
870 rv = APR_EGENERAL;
872 else if (strncmp(MS_NOT_FOUND, conn->buffer, MS_NOT_FOUND_LEN) == 0) {
873 rv = APR_NOTFOUND;
875 else {
876 if (new_value) {
877 *new_value = atoi(conn->buffer);
879 rv = APR_SUCCESS;
882 ms_release_conn(ms, conn);
884 return rv;
887 APR_DECLARE(apr_status_t)
888 apr_memcache_incr(apr_memcache_t *mc,
889 const char *key,
890 apr_int32_t inc,
891 apr_uint32_t *new_value)
893 return num_cmd_write(mc,
894 MC_INCR,
895 MC_INCR_LEN,
896 key,
897 inc,
898 new_value);
902 APR_DECLARE(apr_status_t)
903 apr_memcache_decr(apr_memcache_t *mc,
904 const char *key,
905 apr_int32_t inc,
906 apr_uint32_t *new_value)
908 return num_cmd_write(mc,
909 MC_DECR,
910 MC_DECR_LEN,
911 key,
912 inc,
913 new_value);
918 APR_DECLARE(apr_status_t)
919 apr_memcache_version(apr_memcache_server_t *ms,
920 apr_pool_t *p,
921 char **baton)
923 apr_status_t rv;
924 apr_memcache_conn_t *conn;
925 apr_size_t written;
926 struct iovec vec[2];
928 rv = ms_find_conn(ms, &conn);
930 if (rv != APR_SUCCESS) {
931 return rv;
934 /* version\r\n */
935 vec[0].iov_base = MC_VERSION;
936 vec[0].iov_len = MC_VERSION_LEN;
938 vec[1].iov_base = MC_EOL;
939 vec[1].iov_len = MC_EOL_LEN;
941 rv = apr_socket_sendv(conn->sock, vec, 2, &written);
943 if (rv != APR_SUCCESS) {
944 ms_bad_conn(ms, conn);
945 return rv;
948 rv = get_server_line(conn);
949 if (rv != APR_SUCCESS) {
950 ms_bad_conn(ms, conn);
951 return rv;
954 if (strncmp(MS_VERSION, conn->buffer, MS_VERSION_LEN) == 0) {
955 *baton = apr_pstrmemdup(p, conn->buffer+MS_VERSION_LEN+1,
956 conn->blen - MS_VERSION_LEN - 2);
957 rv = APR_SUCCESS;
959 else {
960 rv = APR_EGENERAL;
963 ms_release_conn(ms, conn);
965 return rv;
968 apr_status_t mc_version_ping(apr_memcache_server_t *ms)
970 apr_status_t rv;
971 apr_size_t written;
972 struct iovec vec[2];
973 apr_memcache_conn_t *conn;
975 rv = ms_find_conn(ms, &conn);
977 if (rv != APR_SUCCESS) {
978 return rv;
981 /* version\r\n */
982 vec[0].iov_base = MC_VERSION;
983 vec[0].iov_len = MC_VERSION_LEN;
985 vec[1].iov_base = MC_EOL;
986 vec[1].iov_len = MC_EOL_LEN;
988 rv = apr_socket_sendv(conn->sock, vec, 2, &written);
990 if (rv != APR_SUCCESS) {
991 ms_bad_conn(ms, conn);
992 return rv;
995 rv = get_server_line(conn);
996 ms_release_conn(ms, conn);
997 return rv;
1002 * Define all of the strings for stats
1005 #define STAT_pid MS_STAT " pid "
1006 #define STAT_pid_LEN (sizeof(STAT_pid)-1)
1008 #define STAT_uptime MS_STAT " uptime "
1009 #define STAT_uptime_LEN (sizeof(STAT_uptime)-1)
1011 #define STAT_time MS_STAT " time "
1012 #define STAT_time_LEN (sizeof(STAT_time)-1)
1014 #define STAT_version MS_STAT " version "
1015 #define STAT_version_LEN (sizeof(STAT_version)-1)
1017 #define STAT_rusage_user MS_STAT " rusage_user "
1018 #define STAT_rusage_user_LEN (sizeof(STAT_rusage_user)-1)
1020 #define STAT_rusage_system MS_STAT " rusage_system "
1021 #define STAT_rusage_system_LEN (sizeof(STAT_rusage_system)-1)
1023 #define STAT_curr_items MS_STAT " curr_items "
1024 #define STAT_curr_items_LEN (sizeof(STAT_curr_items)-1)
1026 #define STAT_total_items MS_STAT " total_items "
1027 #define STAT_total_items_LEN (sizeof(STAT_total_items)-1)
1029 #define STAT_bytes MS_STAT " bytes "
1030 #define STAT_bytes_LEN (sizeof(STAT_bytes)-1)
1032 #define STAT_curr_connections MS_STAT " curr_connections "
1033 #define STAT_curr_connections_LEN (sizeof(STAT_curr_connections)-1)
1035 #define STAT_total_connections MS_STAT " total_connections "
1036 #define STAT_total_connections_LEN (sizeof(STAT_total_connections)-1)
1038 #define STAT_connection_structures MS_STAT " connection_structures "
1039 #define STAT_connection_structures_LEN (sizeof(STAT_connection_structures)-1)
1041 #define STAT_cmd_get MS_STAT " cmd_get "
1042 #define STAT_cmd_get_LEN (sizeof(STAT_cmd_get)-1)
1044 #define STAT_cmd_set MS_STAT " cmd_set "
1045 #define STAT_cmd_set_LEN (sizeof(STAT_cmd_set)-1)
1047 #define STAT_get_hits MS_STAT " get_hits "
1048 #define STAT_get_hits_LEN (sizeof(STAT_get_hits)-1)
1050 #define STAT_get_misses MS_STAT " get_misses "
1051 #define STAT_get_misses_LEN (sizeof(STAT_get_misses)-1)
1053 #define STAT_bytes_read MS_STAT " bytes_read "
1054 #define STAT_bytes_read_LEN (sizeof(STAT_bytes_read)-1)
1056 #define STAT_bytes_written MS_STAT " bytes_written "
1057 #define STAT_bytes_written_LEN (sizeof(STAT_bytes_written)-1)
1059 #define STAT_limit_maxbytes MS_STAT " limit_maxbytes "
1060 #define STAT_limit_maxbytes_LEN (sizeof(STAT_limit_maxbytes)-1)
1063 static const char *stat_read_string(apr_pool_t *p, char *buf, int len)
1065 /* remove trailing \r\n and null char */
1066 return apr_pstrmemdup(p, buf, len-2);
1069 static apr_uint32_t stat_read_uint32(apr_pool_t *p, char *buf, int len)
1071 buf[len-2] = '\0';
1072 return atoi(buf);
1075 static apr_uint64_t stat_read_uint64(apr_pool_t *p, char *buf, int len)
1077 buf[len-2] = '\0';
1078 return apr_atoi64(buf);
1081 static apr_time_t stat_read_time(apr_pool_t *p, char *buf, int len)
1083 buf[len-2] = '\0';
1084 return apr_time_from_sec(atoi(buf));
1087 static apr_time_t stat_read_rtime(apr_pool_t *p, char *buf, int len)
1089 char *tok;
1090 char *secs;
1091 char *usecs;
1092 const char *sep = ":";
1094 buf[len-2] = '\0';
1096 secs = apr_strtok(buf, sep, &tok);
1097 if (secs == NULL) {
1098 sep = ".";
1099 secs = apr_strtok(buf, sep, &tok);
1101 usecs = apr_strtok(NULL, sep, &tok);
1102 if (secs && usecs) {
1103 return apr_time_make(atoi(secs), atoi(usecs));
1105 else {
1106 return apr_time_make(0, 0);
1111 * I got tired of Typing. Meh.
1113 * TODO: Convert it to static tables to make it cooler.
1116 #define mc_stat_cmp(name) \
1117 strncmp(STAT_ ## name, conn->buffer, STAT_ ## name ## _LEN) == 0
1119 #define mc_stat_str(name) \
1120 stat_read_string(p, conn->buffer + name, \
1121 conn->blen - name)
1123 #define mc_stat_uint32(name) \
1124 stat_read_uint32(p, conn->buffer + name, \
1125 conn->blen - name)
1127 #define mc_stat_uint64(name) \
1128 stat_read_uint64(p, conn->buffer + name, \
1129 conn->blen - name)
1131 #define mc_stat_time(name) \
1132 stat_read_time(p, conn->buffer + name, \
1133 conn->blen - name)
1135 #define mc_stat_rtime(name) \
1136 stat_read_rtime(p, conn->buffer + name, \
1137 conn->blen - name)
1140 #define mc_do_stat(name, type) \
1141 if (mc_stat_cmp(name)) { \
1142 stats-> name = mc_stat_ ## type ((STAT_ ## name ## _LEN)); \
1145 static void update_stats(apr_pool_t *p, apr_memcache_conn_t *conn,
1146 apr_memcache_stats_t *stats)
1149 mc_do_stat(version, str)
1150 else mc_do_stat(pid, uint32)
1151 else mc_do_stat(uptime, uint32)
1152 else mc_do_stat(time, time)
1153 else mc_do_stat(rusage_user, rtime)
1154 else mc_do_stat(rusage_system, rtime)
1155 else mc_do_stat(curr_items, uint32)
1156 else mc_do_stat(total_items, uint32)
1157 else mc_do_stat(bytes, uint64)
1158 else mc_do_stat(curr_connections, uint32)
1159 else mc_do_stat(total_connections, uint32)
1160 else mc_do_stat(connection_structures, uint32)
1161 else mc_do_stat(cmd_get, uint32)
1162 else mc_do_stat(cmd_set, uint32)
1163 else mc_do_stat(get_hits, uint32)
1164 else mc_do_stat(get_misses, uint32)
1165 else mc_do_stat(bytes_read, uint64)
1166 else mc_do_stat(bytes_written, uint64)
1167 else mc_do_stat(limit_maxbytes, uint32)
1170 APR_DECLARE(apr_status_t)
1171 apr_memcache_stats(apr_memcache_server_t *ms,
1172 apr_pool_t *p,
1173 apr_memcache_stats_t **stats)
1175 apr_memcache_stats_t *ret;
1176 apr_status_t rv;
1177 apr_memcache_conn_t *conn;
1178 apr_size_t written;
1179 struct iovec vec[2];
1181 rv = ms_find_conn(ms, &conn);
1183 if (rv != APR_SUCCESS) {
1184 return rv;
1187 /* version\r\n */
1188 vec[0].iov_base = MC_STATS;
1189 vec[0].iov_len = MC_STATS_LEN;
1191 vec[1].iov_base = MC_EOL;
1192 vec[1].iov_len = MC_EOL_LEN;
1194 rv = apr_socket_sendv(conn->sock, vec, 2, &written);
1196 if (rv != APR_SUCCESS) {
1197 ms_bad_conn(ms, conn);
1198 return rv;
1201 ret = apr_pcalloc(p, sizeof(apr_memcache_stats_t));
1203 do {
1204 rv = get_server_line(conn);
1205 if (rv != APR_SUCCESS) {
1206 ms_bad_conn(ms, conn);
1207 return rv;
1210 if (strncmp(MS_END, conn->buffer, MS_END_LEN) == 0) {
1211 rv = APR_SUCCESS;
1212 break;
1214 else if (strncmp(MS_STAT, conn->buffer, MS_STAT_LEN) == 0) {
1215 update_stats(p, conn, ret);
1216 continue;
1218 else {
1219 rv = APR_EGENERAL;
1220 break;
1223 } while(1);
1225 ms_release_conn(ms, conn);
1227 if (stats) {
1228 *stats = ret;
1231 return rv;