1 /*
2 * Copyright (c) 2004-2005 Sean Chittenden <sean@chittenden.org>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 /* The crc32 functions and data was originally written by Spencer
26 * Garrett <srg@quick.com> and was cleaned from the PostgreSQL source
27 * tree via the files contrib/ltree/crc32.[ch]. No license was
28 * included, therefore it is assumed that this code is public
29 * domain. Attribution still noted. */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34 #include <ctype.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <sysexits.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #ifdef __linux
43 # ifndef __USE_POSIX
44 # define __USE_POSIX
45 #warning "Working around busted-ass Linux header include problems: use FreeBSD instead"
46 #warning "http://www.FreeBSD.org/ - you won't regret it"
47 # endif
48 #endif
49 #include <sys/time.h>
50 #include <sys/socket.h>
51 #include <sys/uio.h>
52 #include <netdb.h>
53 #include <netinet/in.h>
54 #include <netinet/tcp.h>
55 #include <unistd.h>
56 #include <fcntl.h>
57 #include <arpa/inet.h>
58
59 #define __MEMCACHE__
60 # include "memcache.h"
61 # include "memcache/buffer.h"
62 #undef __MEMCACHE__
63
64 #ifdef MAX
65 # undef MAX
66 #endif
67 #define MAX(a,b) (((a)>(b))?(a):(b))
68
69 #ifdef MIN
70 # undef MIN
71 #endif
72 #define MIN(a,b) (((a)<(b))?(a):(b))
73
74
75 /* Prototypes for static functions that are mcm_*() safe, but don't
76 * require a memory context. */
77 static void mcm_server_init(const struct memcache_ctxt *ctxt, struct memcache_server *ms);
78
79 /* Prototypes for static functions that require a memory context */
80 static u_int32_t mcm_atomic_cmd(struct memcache_ctxt *ctxt, struct memcache *mc,
81 const char *cmd, const size_t cmd_len,
82 char *key, const size_t key_len, const u_int32_t val);
83 static int32_t mcm_err_func(MCM_ERR_FUNC_SIG);
84 static void mcm_fetch_cmd(struct memcache_ctxt *ctxt, struct memcache *mc,
85 struct memcache_req *req, const char *cmd, const size_t cmd_len);
86 static char *mcm_get_line(struct memcache_ctxt *ctxt, struct memcache *mc,
87 struct memcache_server *ms);
88 static u_int32_t mcm_hash_key_func(MCM_HASH_SIG);
89 static size_t mcm_read_fd(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms, char *buf, size_t bytes);
90 static void mcm_res_cb_free(struct memcache_req *req, struct memcache_res_cb *cb);
91 static struct memcache_res *mcm_res_new(const struct memcache_ctxt *ctxt);
92 static int mcm_server_connect(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms);
93 static struct memcache_server *mcm_server_connect_next_avail(struct memcache_ctxt *ctxt, struct memcache *mc, const u_int32_t hash);
94 static void *mcm_server_find_func(const void *ctxt, void *mc, const u_int32_t hash);
95 static int mcm_server_readable(struct memcache_ctxt *ctxt, struct memcache_server *ms, struct timeval *tv);
96 static int mcm_server_resolve(struct memcache_ctxt *ctxt, struct memcache_server *ms);
97 static size_t mcm_server_send_cmd(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms);
98 inline static ssize_t mcm_server_send_last_cmd(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms);
99 static struct memcache_server_stats *mcm_server_stats_new(const struct memcache_ctxt *ctxt);
100 static int mcm_server_writable(struct memcache_ctxt *ctxt, struct memcache_server *ms, struct timeval *tv);
101 static int mcm_storage_cmd(struct memcache_ctxt *ctxt, struct memcache *mc,
102 const char *cmd, const size_t cmd_len,
103 char *key, const size_t key_len,
104 const void *val, const size_t bytes,
105 const time_t expire, const u_int16_t flags);
106 inline static int32_t mcm_validate_key(const struct memcache_ctxt *ctxt, char *key, size_t len);
107 static int32_t mcm_validate_key_func(MCM_KEY_VALID_FUNC_SIG);
108
109 /* Simple macro to test the input of keys. */
110 #define MCM_VALIDATE_KEY(_key, _len) do { int32_t _validate_ret; \
111 _validate_ret = mcm_validate_key(ctxt, _key, _len); \
112 if (_validate_ret != 0) return _validate_ret; \
113 } while(0);
114
115 #define MCM_VALIDATE_KEY_RET(_key, _len, _ret) do { int32_t _validate_ret; \
116 _validate_ret = mcm_validate_key(ctxt, _key, _len); \
117 if (_validate_ret != 0) return _ret; \
118 } while(0);
119
120 #define MCM_CLEAN_BUFS(ctxt, ms) do { \
121 if (ms->rbuf->off == ms->rbuf->len) \
122 mcm_buf_reset(ctxt, ms->rbuf); \
123 if (ms->wbuf->off == ms->wbuf->len) \
124 mcm_buf_reset(ctxt, ms->wbuf); \
125 } while(0)
126
127
128 /* This is kinda ugly, but, it saves on some warnings and a tad of
129 * stack space across the library. Note, remember strlen(3) does not
130 * include the trailing null character, but sizeoof() does, so when
131 * computing the sizeof() commands, subtract one from its return. */
132 static const char str_add_cmd[] = "add ";
133 static const size_t str_add_len = MCM_CSTRLEN(str_add_cmd);
134 static const char str_decr_cmd[] = "decr ";
135 static const size_t str_decr_len = MCM_CSTRLEN(str_decr_cmd);
136 static const char str_delete_cmd[] = "delete ";
137 static const size_t str_delete_len = MCM_CSTRLEN(str_delete_cmd);
138 static const char str_endl[] = "\r\n";
139 static const size_t str_endl_len = MCM_CSTRLEN(str_endl);
140 static const char str_get_cmd[] = "get ";
141 static const size_t str_get_len = MCM_CSTRLEN(str_get_cmd);
142 static const char str_incr_cmd[] = "incr ";
143 static const size_t str_incr_len = MCM_CSTRLEN(str_incr_cmd);
144 #ifdef SEAN_HACKS
145 static const char str_listen_cmd[] = "listen ";
146 static const size_t str_listen_len = MCM_CSTRLEN(str_listen_cmd);
147 static const char str_refresh_cmd[] = "refresh ";
148 static const size_t str_refresh_len = MCM_CSTRLEN(str_refresh_cmd);
149 #endif
150 static const char str_replace_cmd[] = "replace ";
151 static const size_t str_replace_len = MCM_CSTRLEN(str_replace_cmd);
152 static const char str_set_cmd[] = "set ";
153 static const size_t str_set_len = MCM_CSTRLEN(str_set_cmd);
154 static const char str_space[] = " ";
155 static const size_t str_space_len = MCM_CSTRLEN(str_space);
156
157
158 /* Set the default error handling context. */
159 static struct memcache_err_ctxt mcGlobalECtxt;
160
161 /* Set the default memory handling routines to be system defaults. */
162 static struct memcache_ctxt mcGlobalCtxt = {
163 (mcFreeFunc)free,
164 (mcMallocFunc)malloc,
165 (mcMallocFunc)malloc,
166 (mcReallocFunc)realloc,
167 (mcErrFunc)mcm_err_func,
168 (mcKeyValidFunc)mcm_validate_key_func,
169 (mcHashKeyFunc)mcm_hash_key_func,
170 (mcServerFindFunc)mcm_server_find_func,
171 (u_int32_t)0,
172 (struct memcache_buf *)NULL,
173 (struct memcache_buf *)NULL,
174 (u_int32_t)0,
175 (struct memcache_err_ctxt *)&mcGlobalECtxt,
176 (u_int32_t)0
177 };
178
179
180 int
mc_add(struct memcache * mc,char * key,const size_t key_len,const void * val,const size_t bytes,const time_t expire,const u_int16_t flags)181 mc_add(struct memcache *mc,
182 char *key, const size_t key_len,
183 const void *val, const size_t bytes,
184 const time_t expire, const u_int16_t flags) {
185 return mcm_storage_cmd(&mcGlobalCtxt, mc, str_add_cmd, str_add_len, key, key_len, val, bytes, expire, flags);
186 }
187
188
189 void *
mc_aget(struct memcache * mc,char * key,const size_t len)190 mc_aget(struct memcache *mc, char *key, const size_t len) {
191 return mcm_aget(&mcGlobalCtxt, mc, key, len);
192 }
193
194
195 void *
mc_aget2(struct memcache * mc,char * key,const size_t len,size_t * retlen)196 mc_aget2(struct memcache *mc, char *key, const size_t len, size_t *retlen) {
197 return mcm_aget2(&mcGlobalCtxt, mc, key, len, retlen);
198 }
199
200
201 #ifdef SEAN_HACKS
202 void *
mc_alisten(struct memcache * mc,char * key,const size_t len)203 mc_alisten(struct memcache *mc, char *key, const size_t len) {
204 return mcm_alisten(&mcGlobalCtxt, mc, key, len);
205 }
206
207
208 void *
mc_arefresh(struct memcache * mc,char * key,const size_t len)209 mc_arefresh(struct memcache *mc, char *key, const size_t len) {
210 return mcm_arefresh(&mcGlobalCtxt, mc, key, len);
211 }
212 #endif
213
214
215 u_int32_t
mc_decr(struct memcache * mc,char * key,const size_t key_len,const u_int32_t val)216 mc_decr(struct memcache *mc, char *key, const size_t key_len, const u_int32_t val) {
217 return mcm_atomic_cmd(&mcGlobalCtxt, mc, str_decr_cmd, str_decr_len, key, key_len, val);
218 }
219
220
221 int
mc_delete(struct memcache * mc,char * key,const size_t key_len,const time_t hold)222 mc_delete(struct memcache *mc, char *key, const size_t key_len, const time_t hold) {
223 return mcm_delete(&mcGlobalCtxt, mc, key, key_len, hold);
224 }
225
226
227 int
mc_err_filter_add(const u_int32_t err_mask)228 mc_err_filter_add(const u_int32_t err_mask) {
229 return mcm_err_filter_add(&mcGlobalCtxt, err_mask);
230 }
231
232
233 int
mc_err_filter_del(const u_int32_t err_mask)234 mc_err_filter_del(const u_int32_t err_mask) {
235 return mcm_err_filter_del(&mcGlobalCtxt, err_mask);
236 }
237
238
239 u_int32_t
mc_err_filter_get(void)240 mc_err_filter_get(void) {
241 return mcm_err_filter_get(&mcGlobalCtxt);
242 }
243
244
245 int
mc_err_filter_test(const u_int32_t err_lvl)246 mc_err_filter_test(const u_int32_t err_lvl) {
247 return mcm_err_filter_test(&mcGlobalCtxt, err_lvl);
248 }
249
250
251 void
mc_err_test(void)252 mc_err_test(void) {
253 mcm_err_test(&mcGlobalCtxt);
254 }
255
256
257 int
mc_flush(struct memcache * mc,struct memcache_server * ms)258 mc_flush(struct memcache *mc, struct memcache_server *ms) {
259 return mcm_flush(&mcGlobalCtxt, mc, ms);
260 }
261
262
263 int
mc_flush_all(struct memcache * mc)264 mc_flush_all(struct memcache *mc) {
265 return mcm_flush_all(&mcGlobalCtxt, mc);
266 }
267
268
269 void
mc_free(struct memcache * mc)270 mc_free(struct memcache *mc) {
271 mcm_free(&mcGlobalCtxt, mc);
272 }
273
274
275 void
mc_get(struct memcache * mc,struct memcache_req * req)276 mc_get(struct memcache *mc, struct memcache_req *req) {
277 mcm_get(&mcGlobalCtxt, mc, req);
278 }
279
280
281 struct memcache_ctxt *
mc_global_ctxt(void)282 mc_global_ctxt(void) {
283 return &mcGlobalCtxt;
284 }
285
286
287 u_int32_t
mc_hash(const struct memcache * mc,const char * key,const size_t len)288 mc_hash(const struct memcache *mc, const char *key, const size_t len) {
289 return mcGlobalCtxt.mcHashKey(&mcGlobalCtxt, mc, key, len);
290 }
291
292
293 u_int32_t
mc_hash_key(const char * key,const size_t len)294 mc_hash_key(const char *key, const size_t len) {
295 return mcGlobalCtxt.mcHashKey(&mcGlobalCtxt, NULL, key, len);
296 }
297
298
299 u_int32_t
mc_incr(struct memcache * mc,char * key,const size_t key_len,const u_int32_t val)300 mc_incr(struct memcache *mc, char *key, const size_t key_len, const u_int32_t val) {
301 return mcm_atomic_cmd(&mcGlobalCtxt, mc, str_incr_cmd, str_incr_len, key, key_len, val);
302 }
303
304
305 struct memcache *
mc_new(void)306 mc_new(void) {
307 return mcm_new(&mcGlobalCtxt);
308 }
309
310
311 #ifdef SEAN_HACKS
312 void
mc_listen(struct memcache * mc,struct memcache_req * req)313 mc_listen(struct memcache *mc, struct memcache_req *req) {
314 mcm_fetch_cmd(&mcGlobalCtxt, mc, req, str_listen_cmd, str_listen_len);
315 }
316
317
318 void
mc_refresh(struct memcache * mc,struct memcache_req * req)319 mc_refresh(struct memcache *mc, struct memcache_req *req) {
320 mcm_fetch_cmd(&mcGlobalCtxt, mc, req, str_refresh_cmd, str_refresh_len);
321 }
322 #endif
323
324
325 u_int32_t
mc_reldate(void)326 mc_reldate(void) {
327 return MEMCACHE_RELDATE;
328 }
329
330
331 int
mc_replace(struct memcache * mc,char * key,const size_t key_len,const void * val,const size_t bytes,const time_t expire,const u_int16_t flags)332 mc_replace(struct memcache *mc,
333 char *key, const size_t key_len,
334 const void *val, const size_t bytes,
335 const time_t expire, const u_int16_t flags) {
336 return mcm_storage_cmd(&mcGlobalCtxt, mc, str_replace_cmd, str_replace_len, key, key_len, val, bytes, expire, flags);
337 }
338
339
340 struct memcache_res *
mc_req_add(struct memcache_req * req,char * key,const size_t len)341 mc_req_add(struct memcache_req *req, char *key, const size_t len) {
342 return mcm_req_add(&mcGlobalCtxt, req, key, len);
343 }
344
345
346 struct memcache_res *
mc_req_add_ref(struct memcache_req * req,char * key,const size_t len)347 mc_req_add_ref(struct memcache_req *req, char *key, const size_t len) {
348 return mcm_req_add_ref(&mcGlobalCtxt, req, key, len);
349 }
350
351
352 void
mc_req_free(struct memcache_req * req)353 mc_req_free(struct memcache_req *req) {
354 mcm_req_free(&mcGlobalCtxt, req);
355 }
356
357
358 struct memcache_req *
mc_req_new(void)359 mc_req_new(void) {
360 return mcm_req_new(&mcGlobalCtxt);
361 }
362
363
364 int
mc_res_attempted(const struct memcache_res * res)365 mc_res_attempted(const struct memcache_res *res) {
366 return mcm_res_attempted(&mcGlobalCtxt, res);
367 }
368
369
370 int
mc_res_found(const struct memcache_res * res)371 mc_res_found(const struct memcache_res *res) {
372 return mcm_res_found(&mcGlobalCtxt, res);
373 }
374
375
376 void
mc_res_free(struct memcache_req * req,struct memcache_res * res)377 mc_res_free(struct memcache_req *req, struct memcache_res *res) {
378 mcm_res_free(&mcGlobalCtxt, req, res);
379 }
380
381
382 void
mc_res_free_on_delete(struct memcache_res * res,const int fod)383 mc_res_free_on_delete(struct memcache_res *res, const int fod) {
384 mcm_res_free_on_delete(&mcGlobalCtxt, res, fod);
385 }
386
387
388 int
mc_res_register_fetch_cb(struct memcache_req * req,struct memcache_res * res,mcResCallback cb,void * misc)389 mc_res_register_fetch_cb(struct memcache_req *req, struct memcache_res *res,
390 mcResCallback cb, void *misc) {
391 return mcm_res_register_fetch_cb(&mcGlobalCtxt, req, res, cb, misc);
392 }
393
394
395 int
mc_server_activate(struct memcache * mc,struct memcache_server * ms)396 mc_server_activate(struct memcache *mc, struct memcache_server *ms) {
397 return mcm_server_activate(&mcGlobalCtxt, mc, ms);
398 }
399
400
401 int
mc_server_activate_all(struct memcache * mc)402 mc_server_activate_all(struct memcache *mc) {
403 return mcm_server_activate_all(&mcGlobalCtxt, mc);
404 }
405
406
407 int
mc_server_add(struct memcache * mc,const char * hostname,const char * port)408 mc_server_add(struct memcache *mc, const char *hostname, const char *port) {
409 return mcm_server_add2(&mcGlobalCtxt, mc, hostname, (hostname != NULL ? strlen(hostname) : 0), port, (port != NULL ? strlen(port) : 0));
410 }
411
412
413 int
mc_server_add2(struct memcache * mc,const char * hostname,const size_t hostname_len,const char * port,const size_t port_len)414 mc_server_add2(struct memcache *mc, const char *hostname, const size_t hostname_len,
415 const char *port, const size_t port_len) {
416 return mcm_server_add2(&mcGlobalCtxt, mc, hostname, hostname_len, port, port_len);
417 }
418
419
420 int
mc_server_add3(struct memcache * mc,struct memcache_server * ms)421 mc_server_add3(struct memcache *mc, struct memcache_server *ms) {
422 return mcm_server_add3(&mcGlobalCtxt, mc, ms);
423 }
424
425
426 int
mc_server_add4(struct memcache * mc,mc_const char * hostport)427 mc_server_add4(struct memcache *mc, mc_const char *hostport) {
428 return mcm_server_add5(&mcGlobalCtxt, mc, hostport, (hostport != NULL ? strlen(hostport) : 0));
429 }
430
431
432 int
mc_server_add5(struct memcache * mc,mc_const char * hostport,const size_t hostlen)433 mc_server_add5(struct memcache *mc, mc_const char *hostport, const size_t hostlen) {
434 return mcm_server_add5(&mcGlobalCtxt, mc, hostport, hostlen);
435 }
436
437
438 void
mc_server_deactivate(struct memcache * mc,struct memcache_server * ms)439 mc_server_deactivate(struct memcache *mc, struct memcache_server *ms) {
440 mcm_server_deactivate(&mcGlobalCtxt, mc, ms);
441 }
442
443
444 void
mc_server_disconnect(struct memcache_server * ms)445 mc_server_disconnect(struct memcache_server *ms) {
446 mcm_server_disconnect(&mcGlobalCtxt, ms);
447 }
448
449
450 void
mc_server_disconnect_all(const struct memcache * mc)451 mc_server_disconnect_all(const struct memcache *mc) {
452 mcm_server_disconnect_all(&mcGlobalCtxt, mc);
453 }
454
455
456 struct memcache_server *
mc_server_find(struct memcache * mc,const u_int32_t hash)457 mc_server_find(struct memcache *mc, const u_int32_t hash) {
458 return (struct memcache_server *)mcGlobalCtxt.mcServerFind(&mcGlobalCtxt, mc, hash);
459 }
460
461
462 void
mc_server_free(struct memcache_server * ms)463 mc_server_free(struct memcache_server *ms) {
464 mcm_server_free(&mcGlobalCtxt, ms);
465 }
466
467
468 struct memcache_server *
mc_server_new(void)469 mc_server_new(void) {
470 return mcm_server_new(&mcGlobalCtxt);
471 }
472
473
474 struct memcache_server_stats *
mc_server_stats(struct memcache * mc,struct memcache_server * ms)475 mc_server_stats(struct memcache *mc, struct memcache_server *ms) {
476 return mcm_server_stats(&mcGlobalCtxt, mc, ms);
477 }
478
479
480 int
mc_server_timeout(struct memcache_server * ms,const int sec,const int msec)481 mc_server_timeout(struct memcache_server *ms, const int sec, const int msec) {
482 return mcm_server_timeout(&mcGlobalCtxt, ms, sec, msec);
483 }
484
485
486 void
mc_server_stats_free(struct memcache_server_stats * s)487 mc_server_stats_free(struct memcache_server_stats *s) {
488 mcm_server_stats_free(&mcGlobalCtxt, s);
489 }
490
491
492 int
mc_set(struct memcache * mc,char * key,const size_t key_len,const void * val,const size_t bytes,const time_t expire,const u_int16_t flags)493 mc_set(struct memcache *mc,
494 char *key, const size_t key_len,
495 const void *val, const size_t bytes,
496 const time_t expire, const u_int16_t flags) {
497 return mcm_storage_cmd(&mcGlobalCtxt, mc, str_set_cmd, str_set_len, key, key_len, val, bytes, expire, flags);
498 }
499
500
501 struct memcache_server_stats *
mc_stats(struct memcache * mc)502 mc_stats(struct memcache *mc) {
503 return mcm_stats(&mcGlobalCtxt, mc);
504 }
505
506
507 char *
mc_strdup(const char * str)508 mc_strdup(const char *str) {
509 return mcm_strndup(&mcGlobalCtxt, str, strlen(str));
510 }
511
512
513 char *
mc_strnchr(mc_const char * str,const int c,const size_t len)514 mc_strnchr(mc_const char *str, const int c, const size_t len) {
515 return mcm_strnchr(&mcGlobalCtxt, str, c, len);
516 }
517
518
519 char *
mc_strndup(const char * str,const size_t len)520 mc_strndup(const char *str, const size_t len) {
521 return mcm_strndup(&mcGlobalCtxt, str, len);
522 }
523
524
525 void
mc_timeout(struct memcache * mc,const int sec,const int msec)526 mc_timeout(struct memcache *mc, const int sec, const int msec) {
527 mcm_timeout(&mcGlobalCtxt, mc, sec, msec);
528 }
529
530
531 u_int32_t
mc_vernum(void)532 mc_vernum(void) {
533 return MEMCACHE_VERNUM;
534 }
535
536
537 const char *
mc_version(void)538 mc_version(void) {
539 return MEMCACHE_VER;
540 }
541 /* END OF THE SINGLE MEMORY CONTEXT API CALLS (ie: mc_*()) */
542
543
544 /* BEGIN MEMORY CONTEXT API (ie: mcm_*()) */
545 int
mcm_add(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t key_len,const void * val,const size_t bytes,const time_t expire,const u_int16_t flags)546 mcm_add(struct memcache_ctxt *ctxt, struct memcache *mc,
547 char *key, const size_t key_len,
548 const void *val, const size_t bytes,
549 const time_t expire, const u_int16_t flags) {
550 return mcm_storage_cmd(ctxt, mc, str_add_cmd, str_add_len, key, key_len, val, bytes, expire, flags);
551 }
552
553
554 /* Issues a "get" command to the memcache server that should contain
555 * the key. The result is mcMalloc(3)'ed and it is assumed that the
556 * caller is required to mcFree(3) the memory. */
557 void *
mcm_aget(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t len)558 mcm_aget(struct memcache_ctxt *ctxt, struct memcache *mc, char *key, const size_t len) {
559 return mcm_aget2(ctxt, mc, key, len, NULL);
560 }
561
562
563 /* Issues a "get" command to the memcache server that should contain
564 * the key. The result is mcMalloc(3)'ed and it is assumed that the
565 * caller is required to mcFree(3) the memory. */
566 void *
mcm_aget2(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t len,size_t * retlen)567 mcm_aget2(struct memcache_ctxt *ctxt, struct memcache *mc, char *key, const size_t len, size_t *retlen) {
568 struct memcache_req *req;
569 struct memcache_res *res;
570 void *ret;
571
572 MCM_VALIDATE_KEY_RET(key, len, NULL);
573 req = mcm_req_new(ctxt);
574 res = mcm_req_add_ref(ctxt, req, key, len);
575 mcm_res_free_on_delete(ctxt, res, 0);
576 mcm_get(ctxt, mc, req);
577 if (retlen != NULL)
578 *retlen = res->bytes;
579 ret = res->val;
580 mcm_req_free(ctxt, req);
581 return ret;
582 }
583
584
585 #ifdef SEAN_HACKS
586 void *
mcm_alisten(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t len)587 mcm_alisten(struct memcache_ctxt *ctxt, struct memcache *mc, char *key, const size_t len) {
588 struct memcache_req *req;
589 struct memcache_res *res;
590 void *ret;
591
592 MCM_VALIDATE_KEY(key, len);
593 req = mcm_req_new(ctxt);
594 res = mcm_req_add_ref(ctxt, req, key, len);
595 mcm_res_free_on_delete(ctxt, res, 0);
596 mcm_listen(ctxt, mc, req);
597 ret = res->val;
598 mcm_req_free(ctxt, req);
599 return ret;
600 }
601
602
603 /* Issues a "refresh" command to the memcache server that should
604 * contain the key. The result is mcMalloc(3)'ed and it is assumed
605 * that the caller is required to mcFree(3) the memory. */
606 void *
mcm_arefresh(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t len)607 mcm_arefresh(struct memcache_ctxt *ctxt, struct memcache *mc, char *key, const size_t len) {
608 struct memcache_req *req;
609 struct memcache_res *res;
610 void *ret;
611
612 MCM_VALIDATE_KEY(key, len);
613 req = mcm_req_new(ctxt);
614 res = mcm_req_add_ref(ctxt, req, key, len);
615 mcm_res_free_on_delete(ctxt, res, 0);
616 mcm_refresh(ctxt, mc, req);
617 ret = res->val;
618 mcm_req_free(ctxt, req);
619 return ret;
620 }
621 #endif
622
623
624 static u_int32_t
mcm_atomic_cmd(struct memcache_ctxt * ctxt,struct memcache * mc,const char * cmd,const size_t cmd_len,char * key,const size_t key_len,const u_int32_t val)625 mcm_atomic_cmd(struct memcache_ctxt *ctxt, struct memcache *mc,
626 const char *cmd, const size_t cmd_len,
627 char *key, const size_t key_len, const u_int32_t val) {
628 struct memcache_server *ms;
629 u_int32_t hash;
630 char *cp, *cur;
631 size_t i;
632 u_int32_t ret;
633 char numbuf[11]; /* 10 == (2 ** 32).to_s.length + '\0'.length */
634
635 /* Reset errnum on re-entry into memcache(3). */
636 ctxt->errnum = 0;
637
638 MCM_VALIDATE_KEY(key, key_len);
639
640 hash = ctxt->mcHashKey(ctxt, mc, key, key_len);
641
642 ms = mcm_server_connect_next_avail(ctxt, mc, hash);
643 if (ms == NULL) {
644 MCM_ERRX(MCM_ERR_MC_VALID_SERVER);
645 return (u_int32_t)MCM_RET_CODE(0);
646 }
647
648 mcm_buf_append(ctxt, ms->wbuf, cmd, cmd_len);
649 mcm_buf_append(ctxt, ms->wbuf, key, key_len);
650 mcm_buf_append_char(ctxt, ms->wbuf, ' ');
651
652 /* Convert the value to a string */
653 i = (size_t)snprintf(numbuf, sizeof(numbuf), "%u", val);
654 if (i < 1) {
655 MCM_ERR(MCM_ERR_LIB_SNPRINTF);
656 MCM_CLEAN_BUFS(ctxt, ms);
657 return (u_int32_t)MCM_RET_CODE(0);
658 }
659
660 mcm_buf_append(ctxt, ms->wbuf, numbuf, i);
661 mcm_buf_append(ctxt, ms->wbuf, str_endl, str_endl_len);
662
663 /* Send the command */
664 if (mcm_server_send_cmd(ctxt, mc, ms) < 0) {
665 MCM_CLEAN_BUFS(ctxt, ms);
666 return 0;
667 }
668
669 cur = mcm_get_line(ctxt, mc, ms);
670 if (cur != NULL && memcmp(cur, "NOT_FOUND", MCM_CSTRLEN("NOT_FOUND")) == 0) {
671 ctxt->errnum = ENOENT;
672 MCM_CLEAN_BUFS(ctxt, ms);
673 return (u_int32_t)MCM_RET_CODE(0);
674 } else if (cur == NULL) {
675 MCM_CLEAN_BUFS(ctxt, ms);
676 return (u_int32_t)MCM_RET_CODE(0);
677 }
678
679
680 /* Try converting the value to an integer. If it succeeds, we've got
681 * a winner. */
682 ret = (u_int32_t)strtol(cur, &cp, 10);
683 if (ret == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
684 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "strtol(3) failed");
685 MCM_CLEAN_BUFS(ctxt, ms);
686 return (u_int32_t)MCM_RET_CODE(0);
687 }
688
689 #ifdef DEBUG_MC_PROTO_ASSERT
690 if (*cp != '\r') {
691 MCM_ERRX(MCM_ERR_PROTO);
692 MCM_CLEAN_BUFS(ctxt, ms);
693 return (u_int32_t)MCM_RET_CODE(0);
694 }
695 #endif
696
697 MCM_CLEAN_BUFS(ctxt, ms);
698 return ret;
699 }
700
701
702 u_int32_t
mcm_decr(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t key_len,const u_int32_t val)703 mcm_decr(struct memcache_ctxt *ctxt, struct memcache *mc, char *key, const size_t key_len, const u_int32_t val) {
704 return mcm_atomic_cmd(ctxt, mc, str_decr_cmd, str_decr_len, key, key_len, val);
705 }
706
707
708 int
mcm_delete(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t key_len,const time_t hold)709 mcm_delete(struct memcache_ctxt *ctxt, struct memcache *mc,
710 char *key, const size_t key_len, const time_t hold) {
711 struct memcache_server *ms;
712 u_int32_t hash;
713 char *cp;
714 size_t i;
715 char numbuf[11]; /* 10 == (2 ** 32).to_s.length + '\0'.length */
716
717 MCM_VALIDATE_KEY(key, key_len);
718
719 /* Reset ctxt->errnum upon entry into memcache(3). */
720 ctxt->errnum = 0;
721
722 hash = ctxt->mcHashKey(ctxt, mc, key, key_len);
723
724 ms = mcm_server_connect_next_avail(ctxt, mc, hash);
725 if (ms == NULL)
726 return (int)MCM_RET_CODE(-1);
727
728 mcm_buf_append(ctxt, ms->wbuf, str_delete_cmd, str_delete_len);
729 mcm_buf_append(ctxt, ms->wbuf, key, key_len);
730
731 /* Only send the hold timer if the value is greater than zero */
732 if (hold != 0) {
733 mcm_buf_append_char(ctxt, ms->wbuf, ' ');
734 /* Convert the value to a string */
735 i = (size_t)snprintf(numbuf, sizeof(numbuf), "%u", (u_int32_t)hold);
736 if (i < 1) {
737 MCM_ERR(MCM_ERR_LIB_SNPRINTF);
738 MCM_CLEAN_BUFS(ctxt, ms);
739 return (int)MCM_RET_CODE(-4);
740 }
741
742 mcm_buf_append(ctxt, ms->wbuf, numbuf, i);
743 }
744
745 mcm_buf_append(ctxt, ms->wbuf, str_endl, str_endl_len);
746
747 if (mcm_server_send_cmd(ctxt, mc, ms) < 0) {
748 MCM_CLEAN_BUFS(ctxt, ms);
749 return (int)MCM_RET_CODE(-3);
750 }
751
752 cp = mcm_get_line(ctxt, mc, ms);
753 if (cp != NULL && memcmp(cp, "DELETED", MCM_CSTRLEN("DELETED")) == 0) {
754 MCM_CLEAN_BUFS(ctxt, ms);
755 return 0;
756 } else if (cp != NULL && memcmp(cp, "NOT_FOUND", MCM_CSTRLEN("NOT_FOUND")) == 0) {
757 MCM_CLEAN_BUFS(ctxt, ms);
758 return 1;
759 } else {
760 MCM_ERRX_MSG(MCM_ERR_PROTO, cp);
761 MCM_CLEAN_BUFS(ctxt, ms);
762 return (int)MCM_RET_CODE(-5);
763 }
764 }
765
766
767 void
mcm_err(const struct memcache_ctxt * ctxt,const u_int32_t flags,const char * funcname,const u_int32_t lineno,const u_int32_t errcode,const char * msg,const u_int32_t msglen,const u_int32_t errlvl)768 mcm_err(const struct memcache_ctxt *ctxt, const u_int32_t flags, const char *funcname, const u_int32_t lineno,
769 const u_int32_t errcode, const char *msg, const u_int32_t msglen, const u_int32_t errlvl) {
770 struct memcache_err_ctxt *ectxt;
771
772 bzero(ctxt->ectxt, sizeof(struct memcache_err_ctxt));
773 ectxt = ctxt->ectxt;
774
775 ectxt->ctxt = ctxt;
776 ectxt->funcname = funcname;
777 ectxt->lineno = lineno;
778 ectxt->errnum = ((flags & NO_ERRNO_FLAG) ? 0 : errno);
779 ectxt->errcode = errcode;
780 ectxt->errmsg = msg;
781 ectxt->errlen = msglen;
782
783 /* Collect all error handling into one place and dispatch handlers
784 * from here. */
785 switch(errcode) {
786 case MCM_ERR_NONE:
787 ectxt->errstr = "no error";
788 ectxt->severity = MCM_ERR_LVL_NONE;
789 ectxt->sysexit = EX_OK;
790 break;
791 case MCM_ERR_ASSERT:
792 ectxt->errstr = "internal memcache(3) assertion";
793 ectxt->severity = MCM_ERR_LVL_FATAL;
794 ectxt->sysexit = EX_SOFTWARE;
795 break;
796 case MCM_ERR_LIB_SNPRINTF:
797 ectxt->errstr = "snprintf(3) failed to convert the value to a string";
798 ectxt->severity = MCM_ERR_LVL_ERR;
799 ectxt->sysexit = EX_DATAERR;
800 break;
801 case MCM_ERR_LIB_STRTOL:
802 ectxt->errstr = "strtol(3) failed";
803 ectxt->severity = MCM_ERR_LVL_ERR;
804 ectxt->sysexit = EX_DATAERR;
805 break;
806 case MCM_ERR_LIB_STRTOLL:
807 ectxt->errstr = "strtoll(3) failed";
808 ectxt->severity = MCM_ERR_LVL_ERR;
809 ectxt->sysexit = EX_DATAERR;
810 break;
811 case MCM_ERR_MC_RECONN:
812 ectxt->errstr = "connection re-established with server";
813 ectxt->severity = MCM_ERR_LVL_INFO;
814 ectxt->sysexit = EX_OK;
815 break;
816 case MCM_ERR_MC_SEND_CMD:
817 ectxt->errstr = "failed to send command to the memcache server";
818 ectxt->severity = MCM_ERR_LVL_NOTICE;
819 ectxt->sysexit = EX_IOERR;
820 break;
821 case MCM_ERR_MC_SERV_LIST:
822 ectxt->errstr = "no available servers in server list";
823 ectxt->severity = MCM_ERR_LVL_WARN;
824 ectxt->sysexit = EX_DATAERR;
825 break;
826 case MCM_ERR_MC_STORE:
827 ectxt->errstr = "unable to store value";
828 ectxt->severity = MCM_ERR_LVL_NOTICE;
829 ectxt->sysexit = EX_CANTCREAT;
830 break;
831 case MCM_ERR_MC_VALID_SERVER:
832 ectxt->errstr = "unable to find a server to connect to";
833 ectxt->severity = MCM_ERR_LVL_NOTICE;
834 ectxt->sysexit = EX_UNAVAILABLE;
835 break;
836 case MCM_ERR_MEM_MALLOC:
837 ectxt->errstr = "mcMalloc(3) failed";
838 ectxt->severity = MCM_ERR_LVL_ERR;
839 ectxt->sysexit = EX_OSERR;
840 break;
841 case MCM_ERR_MEM_REALLOC:
842 ectxt->errstr = "mcRealloc(3) failed";
843 ectxt->severity = MCM_ERR_LVL_ERR;
844 ectxt->sysexit = EX_OSERR;
845 break;
846 case MCM_ERR_NET_CONNECT:
847 ectxt->errstr = "unable to connect to a server";
848 ectxt->severity = MCM_ERR_LVL_NOTICE;
849 ectxt->sysexit = EX_TEMPFAIL;
850 break;
851 case MCM_ERR_NET_HOST:
852 ectxt->errstr = "unable to lookup/resolve host";
853 ectxt->severity = MCM_ERR_LVL_WARN;
854 ectxt->sysexit = EX_NOHOST;
855 break;
856 case MCM_ERR_PROTO:
857 ectxt->errstr = "memcache(4) protocol error";
858 ectxt->severity = MCM_ERR_LVL_FATAL;
859 ectxt->sysexit = EX_PROTOCOL;
860 break;
861 case MCM_ERR_SYS_CLOSE:
862 ectxt->errstr = "close(2) failed";
863 ectxt->severity = MCM_ERR_LVL_ERR;
864 ectxt->sysexit = EX_OSERR;
865 break;
866 case MCM_ERR_SYS_CONNECT:
867 ectxt->errstr = "connect(2) failed";
868 ectxt->severity = MCM_ERR_LVL_NOTICE;
869 ectxt->sysexit = EX_OSERR;
870 break;
871 case MCM_ERR_SYS_FCNTL:
872 ectxt->errstr = "unable to get or set file descriptor status";
873 ectxt->severity = MCM_ERR_LVL_ERR;
874 ectxt->sysexit = EX_OSERR;
875 break;
876 case MCM_ERR_SYS_READ:
877 ectxt->errstr = "read(2) failed";
878 ectxt->severity = MCM_ERR_LVL_ERR;
879 ectxt->sysexit = EX_OSERR;
880 break;
881 case MCM_ERR_SYS_SELECT:
882 ectxt->errstr = "select(2) failed";
883 ectxt->severity = MCM_ERR_LVL_ERR;
884 ectxt->sysexit = EX_OSERR;
885 break;
886 case MCM_ERR_SYS_SETSOCKOPT:
887 ectxt->errstr = "setsockopt(2) failed";
888 ectxt->severity = MCM_ERR_LVL_ERR;
889 ectxt->sysexit = EX_OSERR;
890 break;
891 case MCM_ERR_SYS_SOCKET:
892 ectxt->errstr = "socket(2) failed";
893 ectxt->severity = MCM_ERR_LVL_ERR;
894 ectxt->sysexit = EX_OSERR;
895 break;
896 case MCM_ERR_SYS_WRITEV:
897 ectxt->errstr = "writev(2) failed";
898 ectxt->severity = MCM_ERR_LVL_ERR;
899 ectxt->sysexit = EX_OSERR;
900 case MCM_ERR_TEST:
901 ectxt->errstr = "internal memcache(3) test message";
902 ectxt->severity = MCM_ERR_LVL_WARN;
903 ectxt->sysexit = EX_OK;
904 break;
905 case MCM_ERR_TIMEOUT:
906 ectxt->errstr = "timeout";
907 ectxt->severity = MCM_ERR_LVL_WARN;
908 ectxt->sysexit = EX_UNAVAILABLE;
909 break;
910 case MCM_ERR_TRACE:
911 ectxt->errstr = "memcache(3) trace";
912 ectxt->severity = MCM_ERR_LVL_INFO;
913 ectxt->sysexit = EX_OK;
914 break;
915 case MCM_ERR_UNKNOWN_STAT:
916 ectxt->errstr = "unknown stat variable";
917 ectxt->severity = MCM_ERR_LVL_WARN;
918 ectxt->sysexit = EX_PROTOCOL;
919 break;
920 default:
921 ectxt->errstr = "unknown error code";
922 ectxt->severity = MCM_ERR_LVL_FATAL;
923 ectxt->sysexit = EX_SOFTWARE;
924 }
925
926 /* If we were passed in an error level, override the default
927 * severity. */
928 if (errlvl != 0)
929 ectxt->severity = errlvl;
930
931 /* Apply the error filter and ignore errors from levels that are
932 * ignored. */
933 if ((ctxt->MCM_ERR_MASK & ectxt->severity) != 0)
934 return;
935
936 /* Determine whether or not we continue running depending on the severity */
937 switch (ectxt->severity) {
938 case MCM_ERR_LVL_INFO:
939 case MCM_ERR_LVL_NOTICE:
940 case MCM_ERR_LVL_WARN:
941 ectxt->cont = 'y';
942 break;
943 case MCM_ERR_LVL_ERR:
944 ectxt->cont = 'n';
945 break;
946 case MCM_ERR_LVL_FATAL:
947 default:
948 ectxt->cont = 'a';
949 }
950
951 /* Call the user's handler. Disregard the return value for now, but
952 * have it there for future use. *shrug* */
953 if (ctxt->mcErr != NULL)
954 (void)ctxt->mcErr(ctxt, ctxt->ectxt);
955
956 /* There are a few error codes that require special cases for */
957 switch (errcode) {
958 case MCM_ERR_MC_SERV_LIST:
959 if (ectxt->cont == 'n')
960 ectxt->cont = 'y';
961 break;
962 }
963
964 switch (ectxt->cont) {
965 case 'y':
966 /* Yes: do nothing */
967 break;
968 case 'n':
969 /* No: exit with an error code */
970 exit(ectxt->sysexit);
971 case 'a':
972 /* Abort: do just that, abort(3) */
973 default:
974 abort();
975 }
976 }
977
978
979 int
mcm_err_filter_add(struct memcache_ctxt * ctxt,const u_int32_t err_mask)980 mcm_err_filter_add(struct memcache_ctxt *ctxt, const u_int32_t err_mask) {
981 if ((ctxt->MCM_ERR_MASK & err_mask) == ctxt->MCM_ERR_MASK)
982 return 0;
983
984 ctxt->MCM_ERR_MASK &= err_mask;
985 return 1;
986 }
987
988
989 int
mcm_err_filter_del(struct memcache_ctxt * ctxt,const u_int32_t err_mask)990 mcm_err_filter_del(struct memcache_ctxt *ctxt, const u_int32_t err_mask) {
991 if ((ctxt->MCM_ERR_MASK & err_mask) == ctxt->MCM_ERR_MASK)
992 return 0;
993
994 ctxt->MCM_ERR_MASK &= ~err_mask;
995 return 1;
996 }
997
998
999 u_int32_t
mcm_err_filter_get(const struct memcache_ctxt * ctxt)1000 mcm_err_filter_get(const struct memcache_ctxt *ctxt) {
1001 return ctxt->MCM_ERR_MASK;
1002 }
1003
1004
1005 int
mcm_err_filter_test(const struct memcache_ctxt * ctxt,const u_int32_t err_lvl)1006 mcm_err_filter_test(const struct memcache_ctxt *ctxt, const u_int32_t err_lvl) {
1007 return(((ctxt->MCM_ERR_MASK & err_lvl) != 0) ? 1 : 0);
1008 }
1009
1010
1011 static int32_t
mcm_err_func(MCM_ERR_FUNC_ARGS)1012 mcm_err_func(MCM_ERR_FUNC_ARGS) {
1013 const struct memcache_ctxt *ctxt;
1014 struct memcache_err_ctxt *ectxt;
1015 const char *errno_str, *severity;
1016 struct timeval tv;
1017
1018 MCM_ERR_INIT_CTXT(ctxt, ectxt);
1019
1020 if (ectxt->errnum != 0)
1021 errno_str = strerror(ectxt->errnum);
1022 else
1023 errno_str = NULL;
1024
1025 switch (ectxt->severity) {
1026 case MCM_ERR_LVL_INFO:
1027 severity = "INFO";
1028 break;
1029 case MCM_ERR_LVL_NOTICE:
1030 severity = "NOTICE";
1031 break;
1032 case MCM_ERR_LVL_WARN:
1033 severity = "WARN";
1034 break;
1035 case MCM_ERR_LVL_ERR:
1036 severity = "ERROR";
1037 break;
1038 case MCM_ERR_LVL_FATAL:
1039 severity = "FATAL";
1040 break;
1041 default:
1042 #ifdef DEBUG_MC_PROTO
1043 do {
1044 char *tm;
1045 size_t tml;
1046 tml = asprintf(&tm, "Unknown error severity: %d", ectxt->severity);
1047 if (tml > 0 && tm != NULL) {
1048 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
1049 free(tm);
1050 }
1051 } while(0);
1052 #endif
1053 severity = "UNKNOWN";
1054 }
1055
1056 /*
1057 * Quick explaination of the various bits of text:
1058 *
1059 * ectxt->errmsg - per error message passed along via one of the MCM_*_MSG() macros (optional)
1060 * ectxt->errstr - memcache(3) error string (optional, though almost always set)
1061 * errno_str - errno error string (optional)
1062 */
1063
1064 if (gettimeofday(&tv, NULL) != 0) {
1065 tv.tv_sec = 0;
1066 tv.tv_usec = 0;
1067 }
1068
1069 if (ectxt->errmsg != NULL && errno_str != NULL && ectxt->errmsg != NULL)
1070 fprintf(stderr, "[%s@%d.%06d] %s():%u: %s: %s: %.*s\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno, ectxt->errstr, errno_str, (int)ectxt->errlen, ectxt->errmsg);
1071 else if (ectxt->errmsg == NULL && errno_str != NULL && ectxt->errmsg != NULL)
1072 fprintf(stderr, "[%s@%d.%06d] %s():%u: %s: %.*s\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno, errno_str, (int)ectxt->errlen, ectxt->errmsg);
1073 else if (ectxt->errmsg != NULL && errno_str == NULL && ectxt->errmsg != NULL)
1074 fprintf(stderr, "[%s@%d.%06d] %s():%u: %s: %.*s\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno, ectxt->errstr, (int)ectxt->errlen, ectxt->errmsg);
1075 else if (ectxt->errmsg != NULL && errno_str != NULL && ectxt->errmsg == NULL)
1076 fprintf(stderr, "[%s@%d.%06d] %s():%u: %s: %s\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno, errno_str, ectxt->errstr);
1077 else if (ectxt->errmsg == NULL && errno_str == NULL && ectxt->errmsg != NULL)
1078 fprintf(stderr, "[%s@%d.%06d] %s():%u: %.*s\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno, (int)ectxt->errlen, ectxt->errmsg);
1079 else if (ectxt->errmsg == NULL && errno_str != NULL && ectxt->errmsg == NULL)
1080 fprintf(stderr, "[%s@%d.%06d] %s():%u: %s\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno, errno_str);
1081 else if (ectxt->errmsg != NULL && errno_str == NULL && ectxt->errmsg == NULL)
1082 fprintf(stderr, "[%s@%d.%06d] %s():%u: %s\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno, ectxt->errmsg);
1083 else
1084 fprintf(stderr, "[%s@%d.%06d] %s():%u\n", severity, (int)tv.tv_sec, (int)tv.tv_usec, ectxt->funcname, ectxt->lineno);
1085
1086 return 0;
1087 }
1088
1089
1090 void
mcm_err_test(const struct memcache_ctxt * ctxt)1091 mcm_err_test(const struct memcache_ctxt *ctxt) {
1092 MCM_WARNX(MCM_ERR_TEST, "per-error message specific to this line of code");
1093 }
1094
1095
1096 static void
mcm_fetch_cmd(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_req * req,const char * cmd,const size_t cmd_len)1097 mcm_fetch_cmd(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_req *req,
1098 const char *cmd, const size_t cmd_len) {
1099 struct memcache_res *res;
1100 struct memcache_res_cb *cb;
1101 struct memcache_server *ms;
1102 size_t bytes, len, remain;
1103 u_int16_t flags, retry;
1104 char *cp, *end;
1105
1106 if (req->num_keys == 0)
1107 return;
1108
1109 /* mcm_fetch_cmd() is now wrapped by mcm_get() so that a serial list
1110 * of fetch cmds are run and all keys are guaranteed to goto the
1111 * correct server. */
1112 res = TAILQ_FIRST(&req->query);
1113 if (res->hash == 0)
1114 res->hash = ctxt->mcHashKey(ctxt, mc, res->key, res->len);
1115
1116 ms = mcm_server_connect_next_avail(ctxt, mc, res->hash);
1117 if (ms == NULL)
1118 return;
1119
1120 mcm_buf_append(ctxt, ms->wbuf, cmd, cmd_len);
1121
1122 TAILQ_FOREACH(res, &req->query, entries) {
1123 if (res->hash == 0)
1124 res->hash = ctxt->mcHashKey(ctxt, mc, res->key, res->len);
1125
1126 mcm_buf_append(ctxt, ms->wbuf, res->key, res->len);
1127
1128 if (res->entries.tqe_next != NULL)
1129 mcm_buf_append_char(ctxt, ms->wbuf, ' ');
1130
1131 /* Even though we haven't sent the request, mark the response as
1132 * having been attempted. */
1133 res->_flags |= MCM_RES_ATTEMPTED;
1134
1135 /* While we're looping, might as well see if we should be auto
1136 * deleting any of these keys. */
1137 if ((res->_flags & (MCM_RES_FREE_ON_DELETE | MCM_RES_NO_FREE_ON_DELETE)) ==
1138 (MCM_RES_FREE_ON_DELETE | MCM_RES_NO_FREE_ON_DELETE))
1139 mcm_res_free_on_delete(ctxt, res, (res->size > 0 ? 0 : 1));
1140 }
1141 mcm_buf_append(ctxt, ms->wbuf, str_endl, str_endl_len);
1142
1143 /* Send the command to the server */
1144 if (mcm_server_send_cmd(ctxt, mc, ms) < 0) {
1145 MCM_CLEAN_BUFS(ctxt, ms);
1146 MCM_ERRX_MSG(MCM_ERR_ASSERT, "unable to send command");
1147 return;
1148 }
1149
1150 while(1) {
1151 /* Grab a line of input from the server */
1152 cp = mcm_get_line(ctxt, mc, ms);
1153 if (cp == NULL) {
1154 MCM_ERRX_MSG(MCM_ERR_PROTO, "protocol, expected a response");
1155 return;
1156 }
1157
1158 if (strncmp(cp, "VALUE ", MCM_CSTRLEN("VALUE ")) == 0) {
1159 cp += MCM_CSTRLEN("VALUE ");
1160
1161 /* Find the length of the key */
1162 for (len = 0; cp[len] && cp[len] != ' ';len++);
1163
1164 /* Find the response for this key */
1165 TAILQ_FOREACH(res, &req->query, entries) {
1166 if ((res->_flags & MCM_RES_FOUND) == 0 &&
1167 len == res->len && memcmp(cp, res->key, res->len) == 0) {
1168 res->_flags |= MCM_RES_FOUND;
1169 break;
1170 }
1171 }
1172
1173 /* Bail if we run across a situation where a VALUE comes back
1174 * for a key that we don't have a request for. */
1175 if (res == NULL) {
1176 MCM_ERR_MSG(MCM_ERR_PROTO, "server sent data for key not in request");
1177 goto cleanup;
1178 }
1179
1180 cp += res->len + MCM_CSTRLEN(" ");
1181
1182 /* Parse the flags */
1183 flags = (u_int16_t)strtol(cp, &end, 10);
1184 if (flags == 0 && ((errno == EINVAL && end == mcm_buf_off_ptr(ctxt, ms->rbuf)) || errno == ERANGE)) {
1185 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid flags");
1186 mcm_server_deactivate(ctxt, mc, ms);
1187 goto cleanup;
1188 }
1189 res->flags = flags;
1190 cp += end - cp + MCM_CSTRLEN(" ");
1191
1192 /* Parse the bytes */
1193 bytes = (size_t)strtol(cp, &end, 10);
1194 if (bytes == 0 && ((errno == EINVAL && end == mcm_buf_off_ptr(ctxt, ms->rbuf)) || errno == ERANGE)) {
1195 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid bytes");
1196 mcm_server_deactivate(ctxt, mc, ms);
1197 goto cleanup;
1198 }
1199 res->bytes = bytes;
1200 cp += end - cp + MCM_CSTRLEN("\r\n");
1201
1202 /* If necessary, allocate memory for the response */
1203 if (res->size == 0) {
1204 res->val = ctxt->mcMallocAtomic(res->bytes + MCM_CSTRLEN("\0"));
1205 if (res->val == NULL) {
1206 MCM_ERRX_MSG(MCM_ERR_ASSERT, "memory was not allocated for res->val");
1207 goto cleanup;
1208 }
1209 ((char *)res->val)[res->bytes] = '\0';
1210 res->size = res->bytes;
1211 }
1212
1213 /* Copy what data we can from the end of the buffer (potentially
1214 * all of it) into the value. If we need to read(2) more data,
1215 * do so directly into the response object. */
1216 remain = mcm_buf_remain_off(ctxt, ms->rbuf);
1217 if (remain >= res->bytes && res->size >= res->bytes) {
1218 /* Response for key fully read(2). We can copy the remaining
1219 * data without having to read(2) it off the wire. */
1220 memcpy(res->val, cp, res->bytes);
1221
1222 ms->rbuf->off += res->bytes;
1223 cp = mcm_get_line(ctxt, mc, ms);
1224 if (cp == NULL) {
1225 MCM_ERRX(MCM_ERR_PROTO);
1226 goto cleanup;
1227 }
1228 } else if (res->bytes >= res->size && remain >= res->bytes) {
1229 /* Response for key fully read(2). We can only copy part of
1230 * the data due to the response object's size limitation, but
1231 * we still read(2) in everything off the wire for this given
1232 * response. */
1233 memcpy(res->val, cp, res->size);
1234
1235 /* Set the offset that way mcm_get_line() doesn't incorrectly
1236 * scan through most of the response looking for a newline. */
1237 ms->rbuf->off += res->bytes;
1238 ms->rbuf->flags |= MCM_BUF_OFF_USED;
1239
1240 /* Suck in the \r\n */
1241 cp = mcm_get_line(ctxt, mc, ms);
1242 if (cp == NULL) {
1243 MCM_ERRX(MCM_ERR_PROTO);
1244 goto cleanup;
1245 }
1246 } else if (res->size >= res->bytes && remain < res->bytes) {
1247 /* Response for key partially read(2). We need to read(2) the
1248 * remaining data off the wire and into the response object's
1249 * value ptr. */
1250 memcpy(res->val, cp, remain);
1251
1252 /* Need to read(2) the remaining data for a response */
1253 mcm_read_fd(ctxt, mc, ms, &((char *)res->val)[remain], res->bytes - remain);
1254
1255 /* Suck in the "next" line that way we can scan past "\r\n" */
1256 mcm_buf_reset(ctxt, ms->rbuf);
1257 cp = mcm_get_line(ctxt, mc, ms);
1258 if (cp == NULL) {
1259 MCM_ERRX_MSG(MCM_ERR_PROTO, "unable to read another line");
1260 goto cleanup;
1261 }
1262
1263 #ifdef DEBUG_MC_PROTO_ASSERT
1264 if (memcmp(cp, "\r\n", MCM_CSTRLEN("\r\n")) != 0) {
1265 MCM_ERRX(MCM_ERR_PROTO);
1266 goto cleanup;
1267 }
1268 #endif
1269 } else if (res->size < res->bytes && remain < res->size) {
1270 /* Response for key partially read(2). We can only copy part
1271 * of the data, and the remaining part of data is already in
1272 * buffer. Unwanted data needs to be read(2) off the fd, but
1273 * needs to be discarded. */
1274 memcpy(res->val, cp, remain);
1275
1276 /* Need to read(2) the remaining data for a response */
1277 mcm_read_fd(ctxt, mc, ms, &((char *)res->val)[remain], res->size - remain);
1278
1279 /* Suck in remaining data and make it disappear */
1280 bytes = res->bytes - (res->size - remain);
1281 retry = 0;
1282 do {
1283 bytes = mcm_read_fd(ctxt, mc, ms, mcm_buf_to_cstr(ctxt, ms->rbuf), mcm_buf_size(ctxt, ms->rbuf));
1284 if (retry > 3)
1285 break;
1286 else
1287 retry++;
1288 } while (bytes > 0);
1289
1290 /* Suck in the "next" line that way we can scan past "\r\n" */
1291 mcm_buf_reset(ctxt, ms->rbuf);
1292 cp = mcm_get_line(ctxt, mc, ms);
1293 if (cp == NULL) {
1294 MCM_ERRX_MSG(MCM_ERR_PROTO, "unable to read another line");
1295 goto cleanup;
1296 }
1297 } else {
1298 MCM_ERRX(MCM_ERR_ASSERT);
1299 goto cleanup;
1300 }
1301 } else if (strncmp(cp, "END", MCM_CSTRLEN("END")) == 0) {
1302 /* This END is the result of no matches found from the request */
1303 goto cleanup;
1304 } else {
1305 MCM_ERRX_MSG(MCM_ERR_PROTO, cp);
1306 goto cleanup;
1307 }
1308 }
1309
1310 /* This should never happen as we exit above while(1) via cleanup
1311 * goto code */
1312 cp = mcm_get_line(ctxt, mc, ms);
1313 #ifdef DEBUG_MC_PROTO_ASSERT
1314 if (strncmp(cp, "END", MCM_CSTRLEN("END")) != 0) {
1315 MCM_ERRX(MCM_ERR_PROTO);
1316 goto cleanup;
1317 }
1318 #endif
1319
1320 cleanup:
1321 /* Now that we've finished the IO, fire off any callbacks that are
1322 * registered. */
1323 /* This is only for "shortcut" calls as the other ones don't get cb */
1324 TAILQ_FOREACH(cb, &req->cb, entries) {
1325 (*cb->cb)(cb->ctxt, cb->res, cb->misc);
1326 }
1327
1328 MCM_CLEAN_BUFS(ctxt, ms);
1329 return;
1330 }
1331
1332
1333 int
mcm_flush(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)1334 mcm_flush(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
1335 char *cur;
1336
1337 if (mcm_server_connect(ctxt, mc, ms) == -1)
1338 return (int)MCM_RET_CODE(-1);
1339
1340 mcm_buf_append(ctxt, ms->wbuf, "flush_all\r\n", MCM_CSTRLEN("flush_all\r\n"));
1341
1342 if (mcm_server_send_cmd(ctxt, mc, ms) < 0) {
1343 MCM_CLEAN_BUFS(ctxt, ms);
1344 return (int)MCM_RET_CODE(-2);
1345 }
1346
1347 cur = mcm_get_line(ctxt, mc, ms);
1348 if (cur != NULL && memcmp(cur, "OK", MCM_CSTRLEN("OK")) == 0) {
1349 MCM_CLEAN_BUFS(ctxt, ms);
1350 return 0;
1351 } else {
1352 MCM_ERRX(MCM_ERR_PROTO);
1353 MCM_CLEAN_BUFS(ctxt, ms);
1354 return (int)MCM_RET_CODE(-3);
1355 }
1356 }
1357
1358
1359 int
mcm_flush_all(struct memcache_ctxt * ctxt,struct memcache * mc)1360 mcm_flush_all(struct memcache_ctxt *ctxt, struct memcache *mc) {
1361 struct memcache_server *ms;
1362 int ret = 0,
1363 tret;
1364
1365 for (ms = mc->server_list.tqh_first; ms != NULL; ms = ms->entries.tqe_next) {
1366 tret = mcm_flush(ctxt, mc, ms);
1367
1368 /* Return the error code of the first non-zero value if there is
1369 * one. Not sure if this is correct, but I don't have a better
1370 * idea right now. XXX */
1371 if (tret != 0 && ret == 0)
1372 ret = tret;
1373 }
1374
1375 return ret;
1376 }
1377
1378
1379 void
mcm_free(struct memcache_ctxt * ctxt,struct memcache * mc)1380 mcm_free(struct memcache_ctxt *ctxt, struct memcache *mc) {
1381 struct memcache_server *ms, *tms;
1382
1383 if (mc == NULL)
1384 return;
1385
1386 tms = mc->server_list.tqh_first;
1387 while(tms != NULL) {
1388 ms = tms;
1389 tms = ms->entries.tqe_next;
1390
1391 mcm_server_free(ctxt, ms);
1392 }
1393
1394 if (mc->servers != NULL) {
1395 ctxt->mcFree(mc->servers);
1396 }
1397
1398 ctxt->mcFree(mc);
1399 }
1400
1401
1402 void
mcm_get(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_req * req)1403 mcm_get(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_req *req) {
1404 /* At this point, we can safely assume we're performing a multi-get.
1405 * Pre-calculate what keys map up with which servers in order to
1406 * group requests where possible. Assume the worst/best case
1407 * scenario in that all keys will go to one server.*/
1408 struct memcache_req **psq, *tsq; /* Per-Server-reQuest */
1409 struct memcache_res *psr, *trs; /* Per-Server-Response */
1410 struct memcache_res_cb *cb;
1411 u_int16_t i;
1412
1413 /* Reset ctxt->errnum upon entry into memcache(3), even though
1414 * mcm_get() and its call graph doesn't make use of ctxt->errnum. */
1415 ctxt->errnum = 0;
1416
1417 /* Perform some trickery and call mcm_fetch_cmd() once for every
1418 * server that needs to be queried. Short-circuit execution where
1419 * possible by invoking mcm_fetch_cmd() immediately if there is only
1420 * one key being queried. */
1421 switch (req->num_keys) {
1422 case 0:
1423 return;
1424 case 1:
1425 mcm_fetch_cmd(ctxt, mc, req, str_get_cmd, str_get_len);
1426 return;
1427 }
1428
1429 /* If we're a multi-get but there is only one server, don't bother
1430 * with splitting out the keys to their appropriate server. */
1431 if (mc->num_servers == 0) {
1432 return;
1433 } else if (mc->num_servers == 1) {
1434 mcm_fetch_cmd(ctxt, mc, req, str_get_cmd, str_get_len);
1435 return;
1436 }
1437
1438 /* Create an array of pointers to the per-server request objects.
1439 * Allocate one extra request object as a terminator of the pointer
1440 * array. */
1441 psq = (struct memcache_req**)ctxt->mcMalloc(sizeof(struct memcache_req*) * (mc->num_servers + 1));
1442 if (psq == NULL) {
1443 MCM_ERRX_MSG(MCM_ERR_ASSERT, "memory was not allocated for psq");
1444 return;
1445 }
1446 bzero(psq, sizeof(struct memcache_req*) * (mc->num_servers + 1));
1447
1448 /* Make a first pass through the keys to determine which server they
1449 * belong to. */
1450 for (trs = req->query.tqh_first; trs != NULL; trs = trs->entries.tqe_next) {
1451 psr = mcm_res_new(ctxt);
1452
1453 /* Shallow copy of trs into psr */
1454 psr->key = trs->key;
1455 psr->len = trs->len;
1456 psr->hash = trs->hash;
1457 psr->val = trs->val;
1458 psr->bytes = trs->bytes;
1459 psr->size = trs->size;
1460 psr->flags = trs->flags;
1461
1462 /* No flags for shadow structure: we don't want the key or value
1463 * to be reaped when we cleanup. */
1464 psr->_flags = 0;
1465
1466 mcm_res_free_on_delete(ctxt, psr, 0);
1467
1468 if (psr->hash == 0) {
1469 psr->hash = trs->hash = ctxt->mcHashKey(ctxt, mc, psr->key, psr->len);
1470 }
1471
1472 /* Store a pointer to the original object. */
1473 psr->misc = trs;
1474
1475 /* Append onto the correct request chain for a server. */
1476 tsq = psq[psr->hash % mc->num_servers];
1477 if (tsq == NULL)
1478 tsq = psq[psr->hash % mc->num_servers] = mcm_req_new(ctxt);
1479 TAILQ_INSERT_TAIL(&tsq->query, psr, entries);
1480 tsq->num_keys++;
1481 }
1482
1483 /* Make a second pass through the list of requests and execute the
1484 * fetch command where appropriate. */
1485 for (i = 0; i < mc->num_servers; i++) {
1486 if (psq[i] == NULL || psq[i]->num_keys == 0)
1487 continue;
1488
1489 mcm_fetch_cmd(ctxt, mc, psq[i], str_get_cmd, str_get_len);
1490
1491 /* Copy the important bits back. */
1492 for (psr = psq[i]->query.tqh_first; psr != NULL; psr = psr->entries.tqe_next) {
1493 trs = (struct memcache_res*)psr->misc;
1494 trs->val = psr->val;
1495 trs->bytes = psr->bytes;
1496 trs->size = psr->size;
1497 trs->flags = psr->flags;
1498 trs->_flags |= psr->_flags;
1499 }
1500 }
1501
1502 /* Cleanup */
1503 for (i = 0; i < mc->num_servers; i++) {
1504 if (psq[i] != NULL)
1505 mcm_req_free(ctxt, psq[i]);
1506 }
1507
1508 ctxt->mcFree(psq);
1509
1510 /* Now that we've finished the IO, fire off any callbacks that are
1511 * registered. */
1512 /* This is for "non-shortcut" calls */
1513 TAILQ_FOREACH(cb, &req->cb, entries) {
1514 (*cb->cb)(cb->ctxt, cb->res, cb->misc);
1515 }
1516 }
1517
1518
1519 static char *
mcm_get_line(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)1520 mcm_get_line(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
1521 size_t bytes_read = 0, bytes_scan = 0;
1522 char *end, *line;
1523 int ret;
1524
1525 /* If we haven't initialized an offset, do so. */
1526 if ((ms->rbuf->flags & MCM_BUF_OFF_USED) != MCM_BUF_OFF_USED) {
1527 ms->rbuf->off = 0;
1528 ms->rbuf->flags |= MCM_BUF_OFF_USED;
1529 } else {
1530 bytes_read = mcm_buf_remain_off(ctxt, ms->rbuf);
1531 }
1532
1533 /* Search for a newline starting at the offset */
1534 scan_for_line:
1535 end = memchr(mcm_buf_off_ptr(ctxt, ms->rbuf) + bytes_scan, (int)'\n', bytes_read);
1536 if (end == NULL) {
1537 /* Prevent rescanning of the buffer */
1538 bytes_scan += bytes_read;
1539 } else {
1540 #ifdef DEBUG_MC_PROTO_ASSERT
1541 if (*(end - 1) != '\r') {
1542 MCM_ERRX_MSG(MCM_ERR_PROTO, "no \\r before \\n");
1543 return NULL;
1544 }
1545 #endif
1546
1547 line = mcm_buf_off_ptr(ctxt, ms->rbuf);
1548 ms->rbuf->off += end - line + 1;
1549 return line;
1550 }
1551
1552 /* We were unable to scan for any bytes in our given buffer. Need
1553 * to read(2) in some data. */
1554 read_more:
1555 if (mcm_server_readable(ctxt, ms, &ms->tv)) {
1556 bytes_read = mcm_buf_read(ctxt, ms->rbuf, ms->fd);
1557 } else {
1558 goto resend;
1559 }
1560
1561 if (bytes_read == 0) {
1562 switch (errno) {
1563 case EAGAIN:
1564 case EINTR:
1565
1566 /* Assume a file descriptor can be read(2), but if it can't
1567 * block until we can read(2) from the fd. */
1568 ret = mcm_server_readable(ctxt, ms, &ms->tv);
1569 if (ret < 0) {
1570 mcm_server_deactivate(ctxt, mc, ms);
1571 MCM_ERR_MSG(MCM_ERR_SYS_SELECT, "select returned bogus value");
1572 return NULL;
1573 } else if (ret == 0) {
1574 goto resend;
1575 } else {
1576 goto read_more;
1577 }
1578 case ECONNRESET:
1579 case EINVAL:
1580 resend:
1581 mcm_server_disconnect(ctxt, ms);
1582
1583 /* Reconnect to the same server. If this fails, get the next
1584 * available server. */
1585 if (mcm_server_connect(ctxt, mc, ms) == -1) {
1586 mcm_server_deactivate(ctxt, mc, ms);
1587 ms = mcm_server_connect_next_avail(ctxt, mc, ms->_last_hash);
1588
1589 if (ms == NULL)
1590 return NULL;
1591 } else {
1592 MCM_ERRX(MCM_ERR_MC_RECONN);
1593 }
1594
1595 mcm_server_send_last_cmd(ctxt, mc, ms);
1596 goto read_more;
1597 default:
1598 /* This shouldn't happen and if it does, we're pooched: better
1599 * dump. */
1600 MCM_ERRX_MSG(MCM_ERR_ASSERT, strerror(errno));
1601 return NULL;
1602 }
1603 }
1604
1605 goto scan_for_line;
1606 }
1607
1608
1609 #ifdef USE_CRC32_HASH
1610 #include "crc32_table.h"
1611 #endif /* USE_CRC32_HASH */
1612
1613
1614 u_int32_t
mcm_hash(const struct memcache_ctxt * ctxt,const struct memcache * mc,const char * key,const size_t len)1615 mcm_hash(const struct memcache_ctxt *ctxt, const struct memcache *mc, const char *key, const size_t len) {
1616 return ctxt->mcHashKey(ctxt, mc, key, len);
1617 }
1618
1619
1620 u_int32_t
mcm_hash_key(const struct memcache_ctxt * ctxt,const char * key,const size_t len)1621 mcm_hash_key(const struct memcache_ctxt *ctxt, const char *key, const size_t len) {
1622 return ctxt->mcHashKey(ctxt, NULL, key, len);
1623 }
1624
1625
1626 static u_int32_t
mcm_hash_key_func(MCM_HASH_FUNC)1627 mcm_hash_key_func(MCM_HASH_FUNC) {
1628 #ifdef USE_CRC32_HASH
1629 const struct memcache_ctxt *ctxt;
1630 const struct memcache *mc;
1631 const char *key;
1632 u_int32_t crc;
1633 size_t len;
1634 size_t i;
1635
1636 MCM_HASH_INIT(ctxt, mc, key, len);
1637 if (mc != NULL && mc->num_servers <= 1)
1638 return 1;
1639
1640 crc = ~0;
1641
1642 for (i = 0; i < len; i++)
1643 crc = (crc >> 8) ^ crc32tab[(crc ^ (key[i])) & 0xff];
1644
1645 crc = (~crc >> 16) & 0x7fff;
1646
1647 return crc == 0 ? 1 : crc;
1648 #else
1649 # ifdef USE_PERL_HASH
1650 const struct memcache_ctxt *ctxt;
1651 const struct memcache *mc;
1652 const char *key;
1653 u_int32_t h, i;
1654 size_t len;
1655 char *p;
1656
1657 MCM_HASH_INIT(ctxt, mc, key, len);
1658 if (mc != NULL && mc->num_servers <= 1)
1659 return 1;
1660
1661 i = len; /* Work back through the key length */
1662 p = key; /* Character pointer */
1663 h = 0; /* The hash value */
1664
1665 while (i--) {
1666 h += *p++;
1667 h += (h << 10);
1668 h ^= (h >> 6);
1669 }
1670 h += (h << 3);
1671 h ^= (h >> 11);
1672 h += (h << 15);
1673
1674 return h == 0 ? 1 : h;
1675 # else
1676 # ifdef USE_ELF_HASH
1677 const struct memcache_ctxt *ctxt;
1678 const struct memcache *mc;
1679 u_int32_t g, h, i;
1680 const char *key;
1681 size_t len;
1682 char *p;
1683
1684 MCM_HASH_INIT(ctxt, mc, key, len);
1685 if (mc != NULL && mc->num_servers <= 1)
1686 return 1;
1687
1688 i = len; /* Work back through the key length */
1689 p = key; /* Character pointer */
1690 h = 0; /* The hash value */
1691
1692 while (i--) {
1693 h = (h << 4) + *p++;
1694 if (g = h & 0xF0000000)
1695 h ^= g >> 24;
1696 h &= ~g;
1697 }
1698
1699 return h == 0 ? 1 : h;
1700 # else
1701 # error "Please choose USE_CRC32_HASH, USE_ELF_HASH, or USE_PERL_HASH as a hashing scheme when compiling memcache"
1702 # endif
1703 # endif
1704 #endif
1705 }
1706
1707
1708 u_int32_t
mcm_incr(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t key_len,const u_int32_t val)1709 mcm_incr(struct memcache_ctxt *ctxt, struct memcache *mc,
1710 char *key, const size_t key_len, const u_int32_t val) {
1711 return mcm_atomic_cmd(ctxt, mc, str_incr_cmd, str_incr_len, key, key_len, val);
1712 }
1713
1714
1715 struct memcache *
mcm_new(struct memcache_ctxt * ctxt)1716 mcm_new(struct memcache_ctxt *ctxt) {
1717 struct memcache *mc;
1718
1719 mc = (struct memcache *)ctxt->mcMalloc(sizeof(struct memcache));
1720 if (mc != NULL) {
1721 bzero(mc, sizeof(struct memcache));
1722
1723 TAILQ_INIT(&mc->server_list);
1724
1725 /* Set any default values */
1726 mc->tv.tv_sec = 2;
1727 mc->tv.tv_usec = 600;
1728 }
1729
1730 return mc;
1731 }
1732
1733
1734 static size_t
mcm_read_fd(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms,char * buf,size_t bytes)1735 mcm_read_fd(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms, char *buf, size_t bytes) {
1736 size_t bytes_read = 0;
1737 ssize_t rb;
1738 int ret;
1739
1740 read_more:
1741 rb = read(ms->fd, buf, bytes);
1742 if (rb < 1) {
1743 switch (errno) {
1744 case EAGAIN:
1745 case EINTR:
1746
1747 /* Assume a file descriptor can be read(2), but if it can't
1748 * block until we can read(2) from the fd. */
1749 ret = mcm_server_readable(ctxt, ms, &ms->tv);
1750 if (ret < 0) {
1751 mcm_server_deactivate(ctxt, mc, ms);
1752 MCM_ERR_MSG(MCM_ERR_SYS_SELECT, "select returned bogus value");
1753 return 0;
1754 } else if (ret == 0) {
1755 mcm_server_disconnect(ctxt, ms);
1756
1757 /* Reconnect to the same server. If this fails, get the next
1758 * available server. */
1759 if (mcm_server_connect(ctxt, mc, ms) == -1) {
1760 mcm_server_deactivate(ctxt, mc, ms);
1761 ms = mcm_server_connect_next_avail(ctxt, mc, ms->_last_hash);
1762
1763 if (ms == NULL)
1764 return 0;
1765 } else {
1766 MCM_ERRX(MCM_ERR_MC_RECONN);
1767 }
1768
1769 mcm_server_send_last_cmd(ctxt, mc, ms);
1770 } else {
1771 goto read_more;
1772 }
1773 default:
1774 /* This shouldn't happen and if it does, we're pooched: better
1775 * dump. */
1776 MCM_ERRX_MSG(MCM_ERR_ASSERT, strerror(errno));
1777 mcm_server_disconnect(ctxt, ms);
1778 return 0;
1779 }
1780 } else {
1781 bytes_read += rb;
1782 buf += rb;
1783 }
1784
1785 /* Need to read(2) more data */
1786 if ((size_t)rb < bytes) {
1787 bytes -= rb;
1788 goto read_more;
1789 } else {
1790 return bytes_read;
1791 }
1792 }
1793
1794
1795 #ifdef SEAN_HACKS
1796 void
mcm_listen(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_req * req)1797 mcm_listen(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_req *req) {
1798 mcm_fetch_cmd(ctxt, mc, req, str_listen_cmd, str_listen_len);
1799 }
1800
1801
1802 void
mcm_refresh(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_req * req)1803 mcm_refresh(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_req *req) {
1804 mcm_fetch_cmd(ctxt, mc, req, str_refresh_cmd, str_refresh_len);
1805 }
1806 #endif
1807
1808
1809 u_int32_t
mcm_reldate(const struct memcache_ctxt * ctxt)1810 mcm_reldate(const struct memcache_ctxt *ctxt) {
1811 return MEMCACHE_RELDATE;
1812 }
1813
1814
1815 int
mcm_replace(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t key_len,const void * val,const size_t bytes,const time_t expire,const u_int16_t flags)1816 mcm_replace(struct memcache_ctxt *ctxt, struct memcache *mc,
1817 char *key, const size_t key_len,
1818 const void *val, const size_t bytes,
1819 const time_t expire, const u_int16_t flags) {
1820 return mcm_storage_cmd(ctxt, mc, str_replace_cmd, str_replace_len, key, key_len, val, bytes, expire, flags);
1821 }
1822
1823
1824 struct memcache_res *
mcm_req_add(const struct memcache_ctxt * ctxt,struct memcache_req * req,char * key,const size_t len)1825 mcm_req_add(const struct memcache_ctxt *ctxt, struct memcache_req *req, char *key, const size_t len) {
1826 struct memcache_res *res;
1827 res = mcm_res_new(ctxt);
1828
1829 MCM_VALIDATE_KEY_RET(key, len, NULL);
1830
1831 res->key = mcm_strdup(ctxt, key);
1832 res->_flags |= MCM_RES_NEED_FREE_KEY;
1833 res->len = len;
1834
1835 TAILQ_INSERT_TAIL(&req->query, res, entries);
1836 req->num_keys++;
1837
1838 return res;
1839 }
1840
1841
1842 struct memcache_res *
mcm_req_add_ref(const struct memcache_ctxt * ctxt,struct memcache_req * req,char * key,const size_t len)1843 mcm_req_add_ref(const struct memcache_ctxt *ctxt, struct memcache_req *req, char *key, const size_t len) {
1844 struct memcache_res *res;
1845 res = mcm_res_new(ctxt);
1846
1847 MCM_VALIDATE_KEY_RET(key, len, NULL);
1848
1849 res->key = key;
1850 res->len = len;
1851
1852 TAILQ_INSERT_TAIL(&req->query, res, entries);
1853 req->num_keys++;
1854
1855 return res;
1856 }
1857
1858
1859 void
mcm_req_free(const struct memcache_ctxt * ctxt,struct memcache_req * req)1860 mcm_req_free(const struct memcache_ctxt *ctxt, struct memcache_req *req) {
1861 while (req->query.tqh_first != NULL)
1862 mcm_res_free(ctxt, req, req->query.tqh_first);
1863
1864 while (req->cb.tqh_first != NULL)
1865 mcm_res_cb_free(req, req->cb.tqh_first);
1866
1867 ctxt->mcFree(req);
1868 }
1869
1870
1871 struct memcache_req *
mcm_req_new(const struct memcache_ctxt * ctxt)1872 mcm_req_new(const struct memcache_ctxt *ctxt) {
1873 struct memcache_req *req;
1874
1875 req = (struct memcache_req *)ctxt->mcMalloc(sizeof(struct memcache_req));
1876 if (req != NULL) {
1877 bzero(req, sizeof(struct memcache_req));
1878
1879 TAILQ_INIT(&req->query);
1880 TAILQ_INIT(&req->cb);
1881 }
1882
1883 return req;
1884 }
1885
1886
1887 int
mcm_res_attempted(const struct memcache_ctxt * ctxt,const struct memcache_res * res)1888 mcm_res_attempted(const struct memcache_ctxt *ctxt,
1889 const struct memcache_res *res) {
1890 return res->_flags & MCM_RES_ATTEMPTED ? 1 : 0;
1891 }
1892
1893
1894 int
mcm_res_found(const struct memcache_ctxt * ctxt,const struct memcache_res * res)1895 mcm_res_found(const struct memcache_ctxt *ctxt,
1896 const struct memcache_res *res) {
1897 return ((res->_flags & (MCM_RES_ATTEMPTED | MCM_RES_FOUND)) == (MCM_RES_ATTEMPTED | MCM_RES_FOUND) ? 1 : 0);
1898 }
1899
1900
1901 void
mcm_res_free(const struct memcache_ctxt * ctxt,struct memcache_req * req,struct memcache_res * res)1902 mcm_res_free(const struct memcache_ctxt *ctxt, struct memcache_req *req, struct memcache_res *res) {
1903 TAILQ_REMOVE(&req->query, res, entries);
1904 if ((res->_flags & MCM_RES_NEED_FREE_KEY) == MCM_RES_NEED_FREE_KEY)
1905 ctxt->mcFree((void *)res->key);
1906
1907 if (((res->_flags & (MCM_RES_FREE_ON_DELETE | MCM_RES_NO_FREE_ON_DELETE)) ==
1908 (MCM_RES_FREE_ON_DELETE | MCM_RES_NO_FREE_ON_DELETE)) ||
1909 res->_flags & MCM_RES_FREE_ON_DELETE) {
1910 if (res->size > 0)
1911 ctxt->mcFree(res->val);
1912 }
1913
1914 ctxt->mcFree(res);
1915 }
1916
1917
1918 void
mcm_res_free_on_delete(const struct memcache_ctxt * ctxt,struct memcache_res * res,const int fod)1919 mcm_res_free_on_delete(const struct memcache_ctxt *ctxt, struct memcache_res *res, const int fod) {
1920 if (fod) {
1921 res->_flags &= ~MCM_RES_NO_FREE_ON_DELETE;
1922 res->_flags |= MCM_RES_FREE_ON_DELETE;
1923 } else {
1924 res->_flags &= ~MCM_RES_FREE_ON_DELETE;
1925 res->_flags |= MCM_RES_NO_FREE_ON_DELETE;
1926 }
1927 }
1928
1929
1930 static struct memcache_res *
mcm_res_new(const struct memcache_ctxt * ctxt)1931 mcm_res_new(const struct memcache_ctxt *ctxt) {
1932 struct memcache_res *res;
1933
1934 res = (struct memcache_res *)ctxt->mcMalloc(sizeof(struct memcache_res));
1935 if (res != NULL) {
1936 bzero(res, sizeof(struct memcache_res));
1937
1938 /* Default values */
1939 res->_flags = MCM_RES_FREE_ON_DELETE | MCM_RES_NO_FREE_ON_DELETE; /* unset */
1940 }
1941
1942 return res;
1943 }
1944
1945
1946 static void
mcm_res_cb_free(struct memcache_req * req,struct memcache_res_cb * cb)1947 mcm_res_cb_free(struct memcache_req *req, struct memcache_res_cb *cb) {
1948 mcFreeFunc freeFunc;
1949
1950 if (cb == NULL || cb->ctxt == NULL)
1951 return;
1952
1953 TAILQ_REMOVE(&req->cb, cb, entries);
1954 freeFunc = cb->ctxt->mcFree;
1955 (freeFunc)(cb);
1956 }
1957
1958
1959 static struct memcache_res_cb *
mcm_res_cb_new(const struct memcache_ctxt * ctxt)1960 mcm_res_cb_new(const struct memcache_ctxt *ctxt) {
1961 struct memcache_res_cb *cb;
1962
1963 cb = (struct memcache_res_cb *)ctxt->mcMalloc(sizeof(struct memcache_res_cb));
1964 if (cb != NULL) {
1965 bzero(cb, sizeof(struct memcache_res_cb));
1966 }
1967
1968 return cb;
1969 }
1970
1971
1972 int
mcm_res_register_fetch_cb(struct memcache_ctxt * ctxt,struct memcache_req * req,struct memcache_res * res,mcResCallback callback,void * misc)1973 mcm_res_register_fetch_cb(struct memcache_ctxt *ctxt, struct memcache_req *req,
1974 struct memcache_res *res, mcResCallback callback, void *misc) {
1975 struct memcache_res_cb *cb;
1976
1977 if (callback == NULL || req == NULL || res == NULL || ctxt == NULL)
1978 return (int)MCM_RET_CODE(-1);
1979
1980 cb = mcm_res_cb_new(ctxt);
1981 if (cb == NULL)
1982 return (int)MCM_RET_CODE(-2);
1983
1984 cb->ctxt = ctxt;
1985 cb->req = req;
1986 cb->cb = callback;
1987 cb->res = res;
1988 cb->misc = misc;
1989
1990 TAILQ_INSERT_TAIL(&req->cb, cb, entries);
1991
1992 return 0;
1993 }
1994
1995
1996 int
mcm_server_activate(const struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)1997 mcm_server_activate(const struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
1998 switch (ms->active) {
1999 case 'd':
2000 ms->active = 'u';
2001 return 0;
2002 case 'n':
2003 MCM_ERRX_MSG(MCM_ERR_ASSERT, "unable to activate a server that does not exist");
2004 return (int)MCM_RET_CODE(-1);
2005 case 't':
2006 MCM_ERRX_MSG_LVL(MCM_ERR_ASSERT, "unable to activate a server that hasn't been added to the server list", MCM_ERR_LVL_INFO);
2007 return (int)MCM_RET_CODE(-2);
2008 case 'u':
2009 MCM_ERRX_MSG_LVL(MCM_ERR_ASSERT, "unable to activate a server that is active", MCM_ERR_LVL_INFO);
2010 return (int)MCM_RET_CODE(-3);
2011 default:
2012 MCM_ERRX(MCM_ERR_ASSERT);
2013 }
2014
2015 MCM_ERRX(MCM_ERR_ASSERT);
2016 return 0;
2017 }
2018
2019
2020 int
mcm_server_activate_all(const struct memcache_ctxt * ctxt,struct memcache * mc)2021 mcm_server_activate_all(const struct memcache_ctxt *ctxt, struct memcache *mc) {
2022 struct memcache_server *ms;
2023
2024 for (ms = mc->server_list.tqh_first; ms != NULL; ms = ms->entries.tqe_next) {
2025 if (ms->active == 'd')
2026 mcm_server_activate(ctxt, mc, ms);
2027 }
2028
2029 return 0;
2030 }
2031
2032
2033 int
mcm_server_add(struct memcache_ctxt * ctxt,struct memcache * mc,const char * hostname,const char * port)2034 mcm_server_add(struct memcache_ctxt *ctxt, struct memcache *mc, const char *hostname, const char *port) {
2035 return mcm_server_add2(ctxt, mc, hostname, (hostname != NULL ? strlen(hostname) : 0), port, (port != NULL ? strlen(port) : 0));
2036 }
2037
2038
2039 int
mcm_server_add2(struct memcache_ctxt * ctxt,struct memcache * mc,const char * hostname,const size_t hostname_len,const char * port,const size_t port_len)2040 mcm_server_add2(struct memcache_ctxt *ctxt, struct memcache *mc, const char *hostname,
2041 const size_t hostname_len, const char *port, const size_t port_len) {
2042 struct memcache_server *ms;
2043
2044 ms = mcm_server_new(ctxt);
2045 if (ms == NULL)
2046 return (int)MCM_RET_CODE(-1);
2047
2048 if (hostname == NULL || hostname_len == 0) {
2049 ms->hostname = mcm_strdup(ctxt, "localhost");
2050 } else {
2051 ms->hostname = mcm_strndup(ctxt, hostname, hostname_len);
2052 }
2053
2054 if (ms->hostname == NULL) {
2055 mcm_server_free(ctxt, ms);
2056 return (int)MCM_RET_CODE(-2);
2057 }
2058
2059
2060 if (port == NULL || port_len == 0) {
2061 ms->port = mcm_strdup(ctxt, "11211");
2062 } else {
2063 ms->port = mcm_strndup(ctxt, port, port_len);
2064 }
2065
2066 if (ms->port == NULL) {
2067 mcm_server_free(ctxt, ms);
2068 return (int)MCM_RET_CODE(-3);
2069 }
2070
2071 return mcm_server_add3(ctxt, mc, ms);
2072 }
2073
2074
2075 int
mcm_server_add3(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)2076 mcm_server_add3(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
2077 int ret;
2078 struct memcache_server **ts;
2079
2080 ret = mcm_server_resolve(ctxt, ms);
2081 if (ret != 0) {
2082 MCM_ERR_MSG(MCM_ERR_NET_HOST, gai_strerror(ret));
2083 mcm_server_free(ctxt, ms);
2084 return (int)MCM_RET_CODE(-4);
2085 }
2086
2087 /* Defaults from mc */
2088 if (ms->tv.tv_sec == 0 && ms->tv.tv_usec == 0) {
2089 ms->tv.tv_sec = mc->tv.tv_sec;
2090 ms->tv.tv_usec = mc->tv.tv_usec;
2091 }
2092
2093 TAILQ_INSERT_TAIL(&mc->server_list, ms, entries);
2094
2095 /* Add ms to the array of servers to try */
2096 if (mc->servers == NULL) {
2097 mc->num_servers = 1;
2098 mc->servers = (struct memcache_server**)ctxt->mcMalloc(sizeof(struct memcache_server*) * (mc->num_servers + 1));
2099 mc->servers[0] = ms;
2100 mc->servers[1] = NULL;
2101 } else {
2102 /* Reallocate mc->servers to fit the number of struct
2103 * memcache_servers entries. 2 == the new memcache server plus an
2104 * additional slot for a NULL server entry. */
2105 ts = (struct memcache_server**)ctxt->mcRealloc(mc->servers, sizeof(struct memcache_server*) * (mc->num_servers + 2));
2106 if (ts == NULL) {
2107 MCM_ERR(MCM_ERR_MEM_REALLOC);
2108 mcm_server_free(ctxt, ms);
2109 return (int)MCM_RET_CODE(-5);
2110 }
2111 mc->servers = ts;
2112
2113 /* Add the new server to the end of the list */
2114 mc->servers[mc->num_servers] = ms;
2115 mc->num_servers++;
2116 mc->servers[mc->num_servers] = NULL;
2117 }
2118
2119 return 0;
2120 }
2121
2122
2123 int
mcm_server_add4(struct memcache_ctxt * ctxt,struct memcache * mc,mc_const char * hostport)2124 mcm_server_add4(struct memcache_ctxt *ctxt, struct memcache *mc, mc_const char *hostport) {
2125 return mcm_server_add5(ctxt, mc, hostport, (hostport != NULL ? strlen(hostport) : 0));
2126 }
2127
2128
2129 int
mcm_server_add5(struct memcache_ctxt * ctxt,struct memcache * mc,mc_const char * hostport,const size_t hostlen)2130 mcm_server_add5(struct memcache_ctxt *ctxt, struct memcache *mc,
2131 mc_const char *hostport, const size_t hostlen) {
2132 struct memcache_server *ms;
2133 char *cp;
2134
2135 ms = mcm_server_new(ctxt);
2136 if (ms == NULL)
2137 return (int)MCM_RET_CODE(-1);
2138
2139 /* Tease out the hostname and portname from a string that we expect
2140 * to look like "host:port". */
2141 if (hostport == NULL || hostlen == 0) {
2142 ms->hostname = mcm_strdup(ctxt, "localhost");
2143 if (ms->hostname == NULL) {
2144 mcm_server_free(ctxt, ms);
2145 return (int)MCM_RET_CODE(-2);
2146 }
2147
2148 ms->port = mcm_strdup(ctxt, "11211");
2149 if (ms->port == NULL) {
2150 mcm_server_free(ctxt, ms);
2151 return (int)MCM_RET_CODE(-3);
2152 }
2153 } else {
2154 cp = mcm_strnchr(ctxt, hostport, ':', hostlen);
2155 if (*cp == '\0') {
2156 ms->hostname = mcm_strndup(ctxt, hostport, hostlen);
2157 if (ms->hostname == NULL) {
2158 mcm_server_free(ctxt, ms);
2159 return (int)MCM_RET_CODE(-2);
2160 }
2161
2162 ms->port = mcm_strdup(ctxt, "11211");
2163 if (ms->port == NULL) {
2164 mcm_server_free(ctxt, ms);
2165 return (int)MCM_RET_CODE(-3);
2166 }
2167 } else {
2168 ms->hostname = mcm_strndup(ctxt, hostport, (size_t)(cp - hostport));
2169 if (ms->hostname == NULL) {
2170 mcm_server_free(ctxt, ms);
2171 return (int)MCM_RET_CODE(-2);
2172 }
2173
2174 /* advance past the ':' and copy whatever is left as the port */
2175 cp++;
2176 ms->port = mcm_strndup(ctxt, cp, hostlen - (size_t)(cp - hostport));
2177 if (ms->port == NULL) {
2178 mcm_server_free(ctxt, ms);
2179 return (int)MCM_RET_CODE(-3);
2180 }
2181 }
2182 }
2183
2184 return mcm_server_add3(ctxt, mc, ms);
2185 }
2186
2187
2188 static int
mcm_server_connect(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)2189 mcm_server_connect(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
2190 struct addrinfo *res;
2191 int flags, i;
2192 #ifdef TCP_NODELAY
2193 int val;
2194 #endif
2195 #ifdef HAVE_SELECT
2196 int ret;
2197 #endif
2198
2199 if (ms->fd != -1)
2200 return ms->fd;
2201
2202 #ifdef DEBUG_MC_PROTO
2203 MCM_WARNX_MSG(MCM_ERR_TRACE, "Begin connect(2)");
2204 #endif
2205
2206 if (ms->active == 'd' || ms->active == 'n')
2207 return (int)MCM_RET_CODE(-1);
2208
2209 if (ms->hostinfo == NULL || ms->hostinfo->ai_addrlen == 0) {
2210 i = mcm_server_resolve(ctxt, ms);
2211 if (i != 0) {
2212 MCM_ERR_MSG(MCM_ERR_NET_HOST, gai_strerror(i));
2213 ms->active = 'n';
2214 return (int)MCM_RET_CODE(-1);
2215 }
2216 }
2217
2218 for (i = 0, res = ms->hostinfo; res != NULL; res = res->ai_next, i++) {
2219 ms->fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
2220 if (ms->fd < 0) {
2221 #ifdef AF_INET6
2222 if (errno == EPROTONOSUPPORT && res->ai_family == AF_INET6)
2223 continue;
2224 #endif
2225 MCM_ERR(MCM_ERR_SYS_SOCKET);
2226 continue;
2227 }
2228
2229 #ifdef TCP_NODELAY
2230 val = 1;
2231 if (setsockopt(ms->fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) != 0) {
2232 MCM_WARN_MSG(MCM_ERR_SYS_SETSOCKOPT, "setsockopt(TCP_NODELAY) failed");
2233 }
2234 #endif
2235
2236 if (mcm_server_timeout(ctxt, ms, ms->tv.tv_sec, ms->tv.tv_usec) == 0) {
2237 mcm_server_disconnect(ctxt, ms);
2238 continue;
2239 }
2240
2241 flags = fcntl(ms->fd, F_GETFL, 0);
2242 if (flags == -1) {
2243 MCM_ERR_MSG(MCM_ERR_SYS_FCNTL, "fcntl(F_GETFL)");
2244 return (int)MCM_RET_CODE(-1);
2245 }
2246
2247 if (fcntl(ms->fd, F_SETFL, flags | O_NONBLOCK) < 0) {
2248 MCM_ERR_MSG(MCM_ERR_SYS_FCNTL, "fcntl(F_SETFL)");
2249 return (int)MCM_RET_CODE(-1);
2250 }
2251
2252 get_conn_status:
2253 ret = connect(ms->fd, res->ai_addr, (socklen_t)res->ai_addrlen);
2254 #ifdef DEBUG_MC_PROTO
2255 do {
2256 char *tm;
2257 size_t tml;
2258 char ch[256], cs[256];
2259
2260 tml = getnameinfo(res->ai_addr, res->ai_addr->sa_len, ch, sizeof(ch), cs, sizeof(cs), NI_NUMERICHOST | NI_NUMERICSERV);
2261 if (tml != 0) {
2262 MCM_WARNX_MSG(MCM_ERR_TRACE, "Unable to get address");
2263 return (int)MCM_RET_CODE(-1);
2264 }
2265
2266 tml = asprintf(&tm, "connect(2) return status for %s:%s: %d", ch, cs, ret);
2267 if (tml > 0 && tm != NULL) {
2268 MCM_WARN_MSG(MCM_ERR_TRACE, tm);
2269 free(tm);
2270 }
2271 } while (0);
2272 #endif
2273 if (ret == 0) {
2274 return ms->fd;
2275 } else {
2276 /* Leave error handling in the event I figure out why connect(2)
2277 * and select(2) weren't returning when polling for a writable
2278 * file descriptor. */
2279 switch (errno) {
2280 case EISCONN:
2281 return ms->fd;
2282 /* Call again, if interrupted */
2283 case EINTR:
2284 goto get_conn_status;
2285 case EINPROGRESS:
2286 ret = mcm_server_writable(ctxt, ms, &ms->tv);
2287 if (ret == -1) {
2288 MCM_ERR_MSG_LVL(MCM_ERR_SYS_SELECT, "select(2) failed for writable status", MCM_ERR_LVL_WARN);
2289 } else if (ret == 0) {
2290 MCM_ERR_MSG_LVL(MCM_ERR_SYS_SELECT, "select(2) timed out on establishing connection", MCM_ERR_LVL_WARN);
2291 printf("connect(): %d\n", connect(ms->fd, res->ai_addr, (socklen_t)res->ai_addrlen));
2292 } else {
2293 goto get_conn_status;
2294 }
2295 /* Fall through */
2296 default:
2297 MCM_ERR(MCM_ERR_SYS_CONNECT);
2298 mcm_server_disconnect(ctxt, ms);
2299 continue;
2300 }
2301 }
2302 }
2303
2304 /* If none of the IP addresses for this hostname work, remove the
2305 * server from the **server list (we assume they're live by default)
2306 * and return -1. */
2307 mcm_server_deactivate(ctxt, mc, ms);
2308 return (int)MCM_RET_CODE(-1);
2309 }
2310
2311
2312 static struct memcache_server *
mcm_server_connect_next_avail(struct memcache_ctxt * ctxt,struct memcache * mc,const u_int32_t hash)2313 mcm_server_connect_next_avail(struct memcache_ctxt *ctxt, struct memcache *mc, const u_int32_t hash) {
2314 struct memcache_server *ms, *nms;
2315
2316 if (mc->num_servers == 0) {
2317 MCM_ERRX(MCM_ERR_MC_SERV_LIST);
2318 return NULL;
2319 }
2320
2321 ms = ctxt->mcServerFind(ctxt, mc, hash);
2322 if (ms == NULL) {
2323 MCM_ERRX(MCM_ERR_MC_VALID_SERVER);
2324 return NULL;
2325 }
2326
2327 while (mcm_server_connect(ctxt, mc, ms) == -1) {
2328 MCM_ERR(MCM_ERR_NET_CONNECT);
2329 mcm_server_deactivate(ctxt, mc, ms);
2330
2331 nms = ctxt->mcServerFind(ctxt, mc, hash);
2332 if (nms == NULL) {
2333 MCM_ERRX(MCM_ERR_MC_SERV_LIST);
2334 return NULL;
2335 }
2336
2337 nms->rbuf= ms->rbuf;
2338 nms->wbuf = ms->wbuf;
2339 ms->rbuf = ms->wbuf = NULL;
2340 ms = nms;
2341 }
2342
2343 ms->_last_hash = ctxt->_last_hash = hash;
2344
2345 /* If there was a present left behind by the last memcache_server,
2346 * assume ownership of the command. */
2347 if (ctxt->_rbuf != NULL || ctxt->_wbuf != NULL) {
2348 ms->rbuf = ctxt->_rbuf;
2349 ms->wbuf = ctxt->_wbuf;
2350 }
2351
2352 return ms;
2353 }
2354
2355
2356 void
mcm_server_deactivate(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)2357 mcm_server_deactivate(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
2358 /* Stash this server's command in the context */
2359 ctxt->_rbuf = ms->rbuf;
2360 ctxt->_wbuf = ms->wbuf;
2361 ctxt->_last_hash = ms->_last_hash;
2362
2363 if (ms->active == 'u' || ms->active == 't')
2364 ms->active = 'd';
2365
2366 mcm_server_disconnect(ctxt, ms);
2367 }
2368
2369
2370 void
mcm_server_disconnect(const struct memcache_ctxt * ctxt,struct memcache_server * ms)2371 mcm_server_disconnect(const struct memcache_ctxt *ctxt, struct memcache_server *ms) {
2372 #ifdef DEBUG_MC_PROTO
2373 MCM_WARNX_MSG(MCM_ERR_TRACE, "Attempting to disconnect");
2374 #endif
2375 if (ms->fd != -1) {
2376 #ifdef DEBUG_MC_PROTO
2377 MCM_WARNX_MSG(MCM_ERR_TRACE, "Closing file descriptor");
2378 #endif
2379 if (close(ms->fd) != 0)
2380 MCM_ERR(MCM_ERR_SYS_CLOSE);
2381 mcm_server_init(ctxt, ms);
2382 }
2383 }
2384
2385
2386 void
mcm_server_disconnect_all(const struct memcache_ctxt * ctxt,const struct memcache * mc)2387 mcm_server_disconnect_all(const struct memcache_ctxt *ctxt, const struct memcache *mc) {
2388 struct memcache_server *ms;
2389
2390 for (ms = mc->server_list.tqh_first; ms != NULL; ms = ms->entries.tqe_next)
2391 mcm_server_disconnect(ctxt, ms);
2392 }
2393
2394
2395 struct memcache_server *
mcm_server_find(const struct memcache_ctxt * ctxt,struct memcache * mc,const u_int32_t hash)2396 mcm_server_find(const struct memcache_ctxt *ctxt, struct memcache *mc, const u_int32_t hash) {
2397 return ((struct memcache_server *)ctxt->mcServerFind(ctxt, mc, hash));
2398 }
2399
2400
2401 static void *
mcm_server_find_func(const void * void_ctxt,void * void_mc,const u_int32_t hash)2402 mcm_server_find_func(const void *void_ctxt, void *void_mc, const u_int32_t hash) {
2403 const struct memcache_ctxt *ctxt;
2404 struct memcache_server *ms = NULL;
2405 struct memcache *mc;
2406 u_int32_t idx, i;
2407
2408 ctxt = (const struct memcache_ctxt *)void_ctxt;
2409 mc = (struct memcache *)void_mc;
2410
2411 if (mc->num_servers < 1)
2412 return NULL;
2413
2414 idx = hash % mc->num_servers;
2415
2416 for (i = 0; i < mc->num_servers; i++) {
2417 /* Grab the correct server from the list. */
2418 ms = mc->servers[idx];
2419
2420 if (ms->active == 'u' || ms->active == 't') {
2421 /* Store the last hash value used to find this server. In the
2422 * event that this server dies, we use this value to
2423 * automatically fall back to the next server. */
2424 ms->_last_hash = hash;
2425
2426 return ms;
2427 } else if (ms->active == 'd') {
2428 /* Try searching for the next server in this list. Remember:
2429 * idx is zero based, but num_servers is one based. */
2430 if (idx + 1 == mc->num_servers)
2431 idx = 0;
2432 else
2433 idx++;
2434
2435 continue;
2436 } else {
2437 MCM_ERRX(MCM_ERR_ASSERT);
2438 return NULL;
2439 }
2440 }
2441
2442 return NULL;
2443 }
2444
2445
2446 void
mcm_server_free(struct memcache_ctxt * ctxt,struct memcache_server * ms)2447 mcm_server_free(struct memcache_ctxt *ctxt, struct memcache_server *ms) {
2448 if (ms == NULL)
2449 return;
2450
2451 if (ms->hostinfo != NULL)
2452 freeaddrinfo(ms->hostinfo);
2453
2454 if (ms->hostname != NULL)
2455 ctxt->mcFree(ms->hostname);
2456
2457 if (ms->port != NULL)
2458 ctxt->mcFree(ms->port);
2459
2460 if (ms->rbuf != NULL)
2461 mcm_buf_free(ctxt, &ms->rbuf);
2462
2463 if (ms->wbuf != NULL)
2464 mcm_buf_free(ctxt, &ms->wbuf);
2465
2466 mcm_server_disconnect(ctxt, ms);
2467
2468 ctxt->mcFree(ms);
2469 }
2470
2471
2472 static void
mcm_server_init(const struct memcache_ctxt * ctxt,struct memcache_server * ms)2473 mcm_server_init(const struct memcache_ctxt *ctxt, struct memcache_server *ms) {
2474 ms->active = 't';
2475 ms->fd = -1;
2476 ms->startoff = ms->soff = 0;
2477 }
2478
2479
2480 struct memcache_server *
mcm_server_new(struct memcache_ctxt * ctxt)2481 mcm_server_new(struct memcache_ctxt *ctxt) {
2482 struct memcache_server *ms;
2483
2484 ms = (struct memcache_server *)ctxt->mcMalloc(sizeof(struct memcache_server));
2485 if (ms != NULL) {
2486 bzero(ms, sizeof(struct memcache_server));
2487
2488 ms->rbuf = mcm_buf_new(ctxt);
2489 if (ms->rbuf == NULL) {
2490 mcm_server_free(ctxt, ms);
2491 return NULL;
2492 }
2493
2494 ms->wbuf = mcm_buf_new(ctxt);
2495 if (ms->wbuf == NULL) {
2496 mcm_server_free(ctxt, ms);
2497 return NULL;
2498 }
2499
2500 /* Set default values */
2501 mcm_server_init(ctxt, ms);
2502 }
2503
2504 return ms;
2505 }
2506
2507
2508 static int
mcm_server_readable(struct memcache_ctxt * ctxt,struct memcache_server * ms,struct timeval * tv)2509 mcm_server_readable(struct memcache_ctxt *ctxt, struct memcache_server *ms, struct timeval *tv) {
2510 #ifndef HAVE_SELECT
2511 return 1;
2512 #else
2513 struct timeval local_tv;
2514 socklen_t so_err_length;
2515 int ret, so_err;
2516
2517 retry_check_readable:
2518
2519 FD_ZERO(&ms->fds);
2520 FD_SET(ms->fd, &ms->fds);
2521
2522 #ifdef DEBUG_MC_PROTO_ASSERT
2523 if (FD_ISSET(ms->fd, &ms->fds) == 0) {
2524 MCM_ERRX(MCM_ERR_ASSERT);
2525 return -1;
2526 }
2527 #endif
2528
2529 memcpy(&local_tv, tv, sizeof(struct timeval));
2530
2531 #ifdef DEBUG_MC_PROTO
2532 do {
2533 char *tm;
2534 size_t tml;
2535 tml = asprintf(&tm, "Begin select(2)'ing to see if fd %d is read(2)able. Timeout %llu.%llu", ms->fd, (u_int64_t)local_tv.tv_sec, (u_int64_t)local_tv.tv_usec);
2536 if (tml > 0 && tm != NULL) {
2537 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
2538 free(tm);
2539 }
2540 } while (0);
2541 #endif
2542
2543 /* Before we read(2) anything, check to make sure there is data
2544 * available to be read(2). No sense in wastefully calling read(2)
2545 * constantly in a loop. */
2546 ret = select(ms->fd + 1, &ms->fds, NULL, NULL, &local_tv);
2547 #ifdef DEBUG_MC_PROTO
2548 do {
2549 char *tm;
2550 size_t tml;
2551 tml = asprintf(&tm, "fd's ready to be read(2): %d/%d", ret, FD_SETSIZE);
2552 if (tml > 0 && tm != NULL) {
2553 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
2554 free(tm);
2555 }
2556 } while (0);
2557 #endif
2558 if (ret > 0) {
2559 /* Check where readable flag is set */
2560 if (!FD_ISSET(ms->fd, &ms->fds)) {
2561 MCM_ERR(MCM_ERR_SYS_SELECT);
2562 return 0;
2563 }
2564
2565 /* Check for the socket errors */
2566 so_err_length = sizeof(so_err);
2567 if (getsockopt(ms->fd, SOL_SOCKET, SO_ERROR, (void *)&so_err, &so_err_length) == -1) {
2568 MCM_ERR(MCM_ERR_SYS_SELECT); /* Socket error */
2569 return 0;
2570 } else {
2571 return ret;
2572 }
2573 } else if (ret == -1) {
2574 switch (errno) {
2575 case EINTR: /* retry this check again */
2576 goto retry_check_readable;
2577 default:
2578 MCM_ERR(MCM_ERR_SYS_SELECT);
2579 return 0;
2580 }
2581 } else if (ret == 0) {
2582 MCM_ERR_MSG(MCM_ERR_TIMEOUT, "select(2) call timed out for read(2)able fds");
2583 return 0;
2584 }
2585
2586 #ifdef DEBUG_MC_PROTO
2587 MCM_WARNX_MSG(MCM_ERR_TRACE, "End select(2)'ing to see if fd is read(2)able");
2588 #endif
2589
2590 return ret;
2591 #endif
2592 }
2593
2594
2595 static int
mcm_server_resolve(struct memcache_ctxt * ctxt,struct memcache_server * ms)2596 mcm_server_resolve(struct memcache_ctxt *ctxt, struct memcache_server *ms) {
2597 struct addrinfo hints, *res;
2598 int ret;
2599
2600 #ifdef DEBUG_MC_PROTO
2601 do {
2602 char *tm;
2603 size_t tml;
2604 tml = asprintf(&tm, "Resolving the host %s:%s", ms->hostname, ms->port);
2605 if (tml > 0 && tm != NULL) {
2606 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
2607 free(tm);
2608 }
2609 } while (0);
2610 #endif
2611
2612 /* Resolve the hostname ahead of time */
2613 bzero(&hints, sizeof(struct addrinfo));
2614 hints.ai_family = PF_UNSPEC;
2615 hints.ai_socktype = SOCK_STREAM;
2616 hints.ai_protocol = IPPROTO_TCP;
2617 ret = getaddrinfo(ms->hostname, ms->port, &hints, &ms->hostinfo);
2618 if (ret != 0) {
2619 #ifdef DEBUG_MC_PROTO
2620 do {
2621 char *tm;
2622 size_t tml;
2623 tml = asprintf(&tm, "getaddrinfo(): %s", gai_strerror(ret));
2624 if (tml > 0 && tm != NULL) {
2625 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
2626 free(tm);
2627 }
2628 } while (0);
2629 #endif
2630
2631 return ret;
2632 }
2633
2634 for (res = ms->hostinfo; res != NULL; res = res->ai_next) {
2635 ms->num_addrs++;
2636 #ifdef DEBUG_MC_PROTO
2637 do {
2638 char *tm, ch[256], cs[256];
2639 size_t tml;
2640 tml = getnameinfo(res->ai_addr, res->ai_addr->sa_len, ch, sizeof(ch), cs, sizeof(cs), NI_NUMERICHOST | NI_NUMERICSERV);
2641 if (tml != 0) {
2642 MCM_WARNX_MSG(MCM_ERR_TRACE, "Unable to get address");
2643 return tml;
2644 }
2645
2646 tml = asprintf(&tm, "Resolved host to \"%s:%s\"", ch, cs);
2647 if (tml > 0 && tm != NULL) {
2648 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
2649 free(tm);
2650 }
2651 } while (0);
2652 #endif
2653
2654 }
2655
2656 return 0;
2657 }
2658
2659
2660 static size_t
mcm_server_send_cmd(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)2661 mcm_server_send_cmd(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
2662 ssize_t ret;
2663
2664 ms->wbuf->off = 0;
2665
2666 #ifdef DEBUG_MC_PROTO
2667 MCM_WARNX_MSG(MCM_ERR_TRACE, "Sending the following data to the server:");
2668 write(fileno(stderr), mcm_buf_to_cstr(ctxt, ms->wbuf), mcm_buf_len(ctxt, ms->wbuf) - ms->wbuf->off);
2669 #endif
2670
2671 write_again:
2672 ret = write(ms->fd, mcm_buf_off_ptr(ctxt, ms->wbuf), mcm_buf_len(ctxt, ms->wbuf) - ms->wbuf->off);
2673 #ifdef DEBUG_MC_PROTO
2674 do {
2675 char *tm;
2676 size_t tml;
2677 tml = asprintf(&tm, "%d = write(2), errno = %d", (int)ret, errno);
2678 if (tml > 0 && tm != NULL) {
2679 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
2680 free(tm);
2681 }
2682 } while (0);
2683 #endif
2684 if (ret < 1) {
2685 switch (errno) {
2686 case EAGAIN:
2687 case EINTR:
2688 case ENOBUFS:
2689 goto write_again;
2690 case EBADF:
2691 case EDESTADDRREQ:
2692 /* Need to reconnect */
2693 MCM_ERR_MSG_LVL(MCM_ERR_MC_SEND_CMD, strerror(errno), MCM_ERR_LVL_INFO);
2694 mcm_server_disconnect(ctxt, ms);
2695
2696 ms = mcm_server_connect_next_avail(ctxt, mc, ms->_last_hash);
2697 goto write_again;
2698 case EDQUOT:
2699 case EFAULT:
2700 case EFBIG:
2701 case EINVAL:
2702 case EIO:
2703 case ENOSPC:
2704 case EPIPE:
2705 default:
2706 MCM_ERR_MSG_LVL(MCM_ERR_MC_SEND_CMD, strerror(errno), MCM_ERR_LVL_FATAL);
2707 mcm_server_deactivate(ctxt, mc, ms);
2708 /* If we're here, the game's up and we can't continue. */
2709 return 0;
2710 }
2711 } else if ((size_t)ret == mcm_buf_len(ctxt, ms->wbuf) - ms->wbuf->off) {
2712 ms->wbuf->off += ret;
2713 return ret;
2714 } else {
2715 ms->wbuf->off += ret;
2716 goto write_again;
2717 }
2718 }
2719
2720
2721 inline static ssize_t
mcm_server_send_last_cmd(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)2722 mcm_server_send_last_cmd(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
2723 mcm_buf_reset(ctxt, ms->rbuf);
2724
2725 return mcm_server_send_cmd(ctxt, mc, ms);
2726 }
2727
2728
2729 struct memcache_server_stats *
mcm_server_stats(struct memcache_ctxt * ctxt,struct memcache * mc,struct memcache_server * ms)2730 mcm_server_stats(struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) {
2731 struct memcache_server_stats *s;
2732 char *cp, *cur;
2733
2734 if (mcm_server_connect(ctxt, mc, ms) == -1)
2735 return NULL;
2736
2737 mcm_buf_append(ctxt, ms->wbuf, "stats\r\n", MCM_CSTRLEN("stats\r\n"));
2738 if (mcm_server_send_cmd(ctxt, mc, ms) < 0) {
2739 MCM_CLEAN_BUFS(ctxt, ms);
2740 return NULL;
2741 }
2742
2743 s = mcm_server_stats_new(ctxt);
2744 if (s == NULL) {
2745 MCM_CLEAN_BUFS(ctxt, ms);
2746 return NULL;
2747 }
2748
2749 for(;;) {
2750 cur = mcm_get_line(ctxt, mc, ms);
2751
2752 if (cur != NULL && memcmp(cur, "STAT ", MCM_CSTRLEN("STAT ")) == 0) {
2753 cur = &cur[MCM_CSTRLEN("STAT ")];
2754
2755 /* Time to loop through the potential stats keys. Joy. This is
2756 * going to complete in O(1 + 2 + 3 ... N) operations (currently
2757 * 190). Ugh. Don't know of a better way to handle this
2758 * without a hash. Besides, this is just stats. */
2759 if (memcmp(cur, "pid ", MCM_CSTRLEN("pid ")) == 0) {
2760 cur = &cur[MCM_CSTRLEN("pid ")];
2761 s->pid = (pid_t)strtol(cur, &cp, 10);
2762 if (s->pid == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2763 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid pid");
2764 MCM_CLEAN_BUFS(ctxt, ms);
2765 return NULL;
2766 } else {
2767 cur = cp;
2768 }
2769 } else if (memcmp(cur, "uptime ", MCM_CSTRLEN("uptime ")) == 0) {
2770 cur = &cur[MCM_CSTRLEN("uptime ")];
2771 s->uptime = (time_t)strtol(cur, &cp, 10);
2772 if (s->uptime == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2773 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid uptime");
2774 MCM_CLEAN_BUFS(ctxt, ms);
2775 return NULL;
2776 } else {
2777 cur = cp;
2778 }
2779 } else if (memcmp(cur, "time ", MCM_CSTRLEN("time ")) == 0) {
2780 cur = &cur[MCM_CSTRLEN("time ")];
2781 s->time = (time_t)strtol(cur, &cp, 10);
2782 if (s->time == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2783 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid time");
2784 MCM_CLEAN_BUFS(ctxt, ms);
2785 return NULL;
2786 } else {
2787 cur = cp;
2788 }
2789 } else if (memcmp(cur, "version ", MCM_CSTRLEN("version ")) == 0) {
2790 cur = &cur[MCM_CSTRLEN("version ")];
2791 for (cp = cur; !isspace(*cp); cp++);
2792 s->version = (char *)ctxt->mcMallocAtomic((size_t)(cp - cur + 1));
2793 if (s->version == NULL) {
2794 MCM_ERR(MCM_ERR_MEM_MALLOC);
2795 } else {
2796 memcpy(s->version, cur, (size_t)(cp - cur));
2797 s->version[(size_t)(cp - cur)] = '\0';
2798 }
2799 } else if (memcmp(cur, "rusage_user ", MCM_CSTRLEN("rusage_user ")) == 0) {
2800 cur = &cur[MCM_CSTRLEN("rusage_user ")];
2801 s->rusage_user.tv_sec = (int32_t)strtol(cur, &cp, 10);
2802 if (s->rusage_user.tv_sec == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2803 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid rusage_user seconds");
2804 MCM_CLEAN_BUFS(ctxt, ms);
2805 return NULL;
2806 } else {
2807 cur = cp;
2808 #ifdef DEBUG_MC_PROTO_ASSERT
2809 if (!(*cur == '.' || *cur == ':'))
2810 MCM_WARNX_MSG(MCM_ERR_PROTO, "invalid separator");
2811 else {
2812 #endif
2813 cur += 1; /* advance past colon */
2814 s->rusage_user.tv_usec = (int32_t)strtol(cur, &cp, 10);
2815 if (s->rusage_user.tv_usec == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2816 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid rusage_user microseconds");
2817 MCM_CLEAN_BUFS(ctxt, ms);
2818 return NULL;
2819 } else {
2820 cur = cp;
2821 }
2822 #ifdef DEBUG_MC_PROTO_ASSERT
2823 }
2824 #endif
2825 }
2826
2827 } else if (memcmp(cur, "rusage_system ", MCM_CSTRLEN("rusage_system ")) == 0) {
2828 cur = &cur[MCM_CSTRLEN("rusage_system ")];
2829 s->rusage_system.tv_sec = (int32_t)strtol(cur, &cp, 10);
2830 if (s->rusage_system.tv_sec == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2831 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid rusage_system seconds");
2832 MCM_CLEAN_BUFS(ctxt, ms);
2833 return NULL;
2834 } else {
2835 cur = cp;
2836 #ifdef DEBUG_MC_PROTO_ASSERT
2837 if (!(*cur == '.' || *cur == ':')) {
2838 MCM_ERR_MSG(MCM_ERR_PROTO, "invalid separator");
2839 MCM_CLEAN_BUFS(ctxt, ms);
2840 return NULL;
2841 } else {
2842 #endif
2843 cur += 1; /* advance past colon */
2844 s->rusage_system.tv_usec = (int32_t)strtol(cur, &cp, 10);
2845 if (s->rusage_system.tv_usec == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2846 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid rusage_system microseconds");
2847 MCM_CLEAN_BUFS(ctxt, ms);
2848 return NULL;
2849 } else {
2850 cur = cp;
2851 }
2852 #ifdef DEBUG_MC_PROTO_ASSERT
2853 }
2854 #endif
2855 }
2856 } else if (memcmp(cur, "curr_items ", MCM_CSTRLEN("curr_items ")) == 0) {
2857 cur = &cur[MCM_CSTRLEN("curr_items ")];
2858 s->curr_items = (u_int32_t)strtol(cur, &cp, 10);
2859 if (s->curr_items == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2860 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid curr_items");
2861 MCM_CLEAN_BUFS(ctxt, ms);
2862 return NULL;
2863 } else {
2864 cur = cp;
2865 }
2866 } else if (memcmp(cur, "total_items ", MCM_CSTRLEN("total_items ")) == 0) {
2867 cur = &cur[MCM_CSTRLEN("total_items ")];
2868 s->total_items = (u_int64_t)strtoll(cur, &cp, 10);
2869 if (s->total_items == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2870 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid total_items");
2871 MCM_CLEAN_BUFS(ctxt, ms);
2872 return NULL;
2873 } else {
2874 cur = cp;
2875 }
2876 } else if (memcmp(cur, "bytes ", MCM_CSTRLEN("bytes ")) == 0) {
2877 cur = &cur[MCM_CSTRLEN("bytes")];
2878 s->bytes = (u_int64_t)strtoll(cur, &cp, 10);
2879 if (s->bytes == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2880 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid bytes");
2881 MCM_CLEAN_BUFS(ctxt, ms);
2882 return NULL;
2883 } else {
2884 cur = cp;
2885 }
2886 } else if (memcmp(cur, "curr_connections ", MCM_CSTRLEN("curr_connections ")) == 0) {
2887 cur = &cur[MCM_CSTRLEN("curr_connections ")];
2888 s->curr_connections = (u_int32_t)strtol(cur, &cp, 10);
2889 if (s->curr_connections == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2890 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid curr_connections");
2891 MCM_CLEAN_BUFS(ctxt, ms);
2892 return NULL;
2893 } else {
2894 cur = cp;
2895 }
2896 } else if (memcmp(cur, "total_connections ", MCM_CSTRLEN("total_connections ")) == 0) {
2897 cur = &cur[MCM_CSTRLEN("total_connections ")];
2898 s->total_connections = (u_int64_t)strtoll(cur, &cp, 10);
2899 if (s->total_connections == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2900 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid total_connections");
2901 MCM_CLEAN_BUFS(ctxt, ms);
2902 return NULL;
2903 } else {
2904 cur = cp;
2905 }
2906 } else if (memcmp(cur, "connection_structures ", MCM_CSTRLEN("connection_structures ")) == 0) {
2907 cur = &cur[MCM_CSTRLEN("connection_structures ")];
2908 s->connection_structures = (u_int32_t)strtol(cur, &cp, 10);
2909 if (s->connection_structures == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2910 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid connection_structures");
2911 MCM_CLEAN_BUFS(ctxt, ms);
2912 return NULL;
2913 } else {
2914 cur = cp;
2915 }
2916 } else if (memcmp(cur, "cmd_get ", MCM_CSTRLEN("cmd_get ")) == 0) {
2917 cur = &cur[MCM_CSTRLEN("cmd_get ")];
2918 s->cmd_get = (u_int64_t)strtoll(cur, &cp, 10);
2919 if (s->cmd_get == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2920 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid cmd_get");
2921 MCM_CLEAN_BUFS(ctxt, ms);
2922 return NULL;
2923 } else {
2924 cur = cp;
2925 }
2926 #ifdef SEAN_HACKS
2927 } else if (memcmp(cur, "cmd_refresh ", MCM_CSTRLEN("cmd_refresh ")) == 0) {
2928 cur = &cur[MCM_CSTRLEN("cmd_refresh ")];
2929 s->cmd_refresh = (u_int64_t)strtoll(cur, &cp, 10);
2930 if (s->cmd_refresh == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2931 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid cmd_refresh");
2932 MCM_CLEAN_BUFS(ctxt, ms);
2933 return NULL;
2934 } else {
2935 cur = cp;
2936 }
2937 #endif
2938 } else if (memcmp(cur, "cmd_set ", MCM_CSTRLEN("cmd_set ")) == 0) {
2939 cur = &cur[MCM_CSTRLEN("cmd_set ")];
2940 s->cmd_set = (u_int64_t)strtoll(cur, &cp, 10);
2941 if (s->cmd_set == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2942 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid cmd_set");
2943 MCM_CLEAN_BUFS(ctxt, ms);
2944 return NULL;
2945 } else {
2946 cur = cp;
2947 }
2948 } else if (memcmp(cur, "get_hits ", MCM_CSTRLEN("get_hits ")) == 0) {
2949 cur = &cur[MCM_CSTRLEN("get_hits ")];
2950 s->get_hits = (u_int64_t)strtoll(cur, &cp, 10);
2951 if (s->get_hits == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2952 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid get_hits");
2953 MCM_CLEAN_BUFS(ctxt, ms);
2954 return NULL;
2955 } else {
2956 cur = cp;
2957 }
2958 } else if (memcmp(cur, "get_misses ", MCM_CSTRLEN("get_misses ")) == 0) {
2959 cur = &cur[MCM_CSTRLEN("get_misses ")];
2960 s->get_misses = (u_int64_t)strtoll(cur, &cp, 10);
2961 if (s->get_misses == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2962 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid get_misses");
2963 MCM_CLEAN_BUFS(ctxt, ms);
2964 return NULL;
2965 } else {
2966 cur = cp;
2967 }
2968 #ifdef SEAN_HACKS
2969 } else if (memcmp(cur, "refresh_hits ", MCM_CSTRLEN("refresh_hits ")) == 0) {
2970 cur = &cur[MCM_CSTRLEN("refresh_hits ")];
2971 s->refresh_hits = (u_int64_t)strtoll(cur, &cp, 10);
2972 if (s->refresh_hits == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2973 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid refresh_hits");
2974 MCM_CLEAN_BUFS(ctxt, ms);
2975 return NULL;
2976 } else {
2977 cur = cp;
2978 }
2979 } else if (memcmp(cur, "refresh_misses ", MCM_CSTRLEN("refresh_misses ")) == 0) {
2980 cur = &cur[MCM_CSTRLEN("refresh_misses ")];
2981 s->refresh_misses = (u_int64_t)strtoll(cur, &cp, 10);
2982 if (s->refresh_misses == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2983 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid refresh_misses");
2984 MCM_CLEAN_BUFS(ctxt, ms);
2985 return NULL;
2986 } else {
2987 cur = cp;
2988 }
2989 #endif
2990 } else if (memcmp(cur, "bytes_read ", MCM_CSTRLEN("bytes_read ")) == 0) {
2991 cur = &cur[MCM_CSTRLEN("bytes_read ")];
2992 s->bytes_read = (u_int64_t)strtoll(cur, &cp, 10);
2993 if (s->bytes_read == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
2994 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid bytes_read");
2995 MCM_CLEAN_BUFS(ctxt, ms);
2996 return NULL;
2997 } else {
2998 cur = cp;
2999 }
3000 } else if (memcmp(cur, "bytes_written ", MCM_CSTRLEN("bytes_written ")) == 0) {
3001 cur = &cur[MCM_CSTRLEN("bytes_written ")];
3002 s->bytes_written = (u_int64_t)strtoll(cur, &cp, 10);
3003 if (s->bytes_written == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
3004 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid bytes_written");
3005 MCM_CLEAN_BUFS(ctxt, ms);
3006 return NULL;
3007 } else {
3008 cur = cp;
3009 }
3010 } else if (memcmp(cur, "limit_maxbytes ", MCM_CSTRLEN("limit_maxbytes ")) == 0) {
3011 cur = &cur[MCM_CSTRLEN("limit_maxbytes ")];
3012 s->limit_maxbytes = (u_int64_t)strtoll(cur, &cp, 10);
3013 if (s->limit_maxbytes == 0 && ((errno == EINVAL && cp == cur) || errno == ERANGE)) {
3014 MCM_ERR_MSG(MCM_ERR_LIB_STRTOL, "invalid limit_maxbytes");
3015 MCM_CLEAN_BUFS(ctxt, ms);
3016 return NULL;
3017 } else {
3018 cur = cp;
3019 }
3020 } else {
3021 for (cp = cur; !isspace(*cp); cp++);
3022 MCM_WARNX_MSGLEN(MCM_ERR_UNKNOWN_STAT, cur, (int)(cp - cur));
3023 MCM_CLEAN_BUFS(ctxt, ms);
3024 return NULL;
3025 }
3026
3027 /* Now that we've sucked in our stats value, set our cursor to
3028 * the end of the value. */
3029 if (!mcm_buf_end(ctxt, ms->rbuf, "\r\n", MCM_CSTRLEN("\r\n"))) {
3030 MCM_ERR_MSG(MCM_ERR_PROTO, "anticipated end of stats value: not at end of stats value");
3031 mcm_server_stats_free(ctxt, s);
3032 mcm_server_deactivate(ctxt, mc, ms);
3033 MCM_CLEAN_BUFS(ctxt, ms);
3034 return NULL;
3035 }
3036 } else if (cur != NULL && memcmp(cur, "END", MCM_CSTRLEN("END")) == 0) {
3037 /* We're done reading in stats. */
3038 break;
3039 } else {
3040 MCM_ERRX_MSG(MCM_ERR_PROTO, "unable to handle response");
3041 MCM_CLEAN_BUFS(ctxt, ms);
3042 return NULL;
3043 }
3044 }
3045
3046 MCM_CLEAN_BUFS(ctxt, ms);
3047 return s;
3048 }
3049
3050
3051 int
mcm_server_timeout(const struct memcache_ctxt * ctxt,struct memcache_server * ms,const int sec,const int msec)3052 mcm_server_timeout(const struct memcache_ctxt *ctxt, struct memcache_server *ms, const int sec, const int msec) {
3053 ms->tv.tv_sec = sec;
3054 ms->tv.tv_usec = msec;
3055
3056 #ifdef USE_SO_SNDTIMEO
3057 /* If any of the setsockopt(2) calls fail, close the socket, set the
3058 * file descriptor to -1, and continue trying to connect to the rest
3059 * of the servers that match this hostname. More than likely there
3060 * is only one IP per host name, but, in the event there isn't,
3061 * continue to the next entry. */
3062 if (setsockopt(ms->fd, SOL_SOCKET, SO_SNDTIMEO, &ms->tv, (socklen_t)sizeof(struct timeval)) != 0) {
3063 MCM_ERR_MSG(MCM_ERR_SYS_SETSOCKOPT, "setsockopt(SO_SNDTIMEO) failed");
3064 return 0;
3065 }
3066 #endif
3067
3068 #ifdef USE_SO_RCVTIMEO
3069 if (setsockopt(ms->fd, SOL_SOCKET, SO_RCVTIMEO, &ms->tv, (socklen_t)sizeof(struct timeval)) != 0) {
3070 MCM_ERR_MSG(MCM_ERR_SYS_SETSOCKOPT, "setsockopt(SO_RCVTIMEO) failed");
3071 return 0;
3072 }
3073 #endif
3074
3075 return 1;
3076 }
3077
3078
3079 void
mcm_server_stats_free(const struct memcache_ctxt * ctxt,struct memcache_server_stats * s)3080 mcm_server_stats_free(const struct memcache_ctxt *ctxt, struct memcache_server_stats *s) {
3081 if (s->version != NULL)
3082 ctxt->mcFree(s->version);
3083 ctxt->mcFree(s);
3084 }
3085
3086
3087 static struct memcache_server_stats *
mcm_server_stats_new(const struct memcache_ctxt * ctxt)3088 mcm_server_stats_new(const struct memcache_ctxt *ctxt) {
3089 struct memcache_server_stats *s;
3090 s = (struct memcache_server_stats *)ctxt->mcMalloc(sizeof(struct memcache_server_stats));
3091 if (s != NULL) {
3092 bzero(s, sizeof(struct memcache_server_stats));
3093 }
3094
3095 return s;
3096 }
3097
3098
3099 static int
mcm_server_writable(struct memcache_ctxt * ctxt,struct memcache_server * ms,struct timeval * tv)3100 mcm_server_writable(struct memcache_ctxt *ctxt, struct memcache_server *ms, struct timeval *tv) {
3101 #ifndef HAVE_SELECT
3102 return 1;
3103 #else
3104 struct timeval local_tv;
3105 socklen_t so_err_length;
3106 int ret, so_err;
3107
3108 retry_check_writable:
3109
3110 FD_ZERO(&ms->fds);
3111 FD_SET(ms->fd, &ms->fds);
3112
3113 #ifdef DEBUG_MC_PROTO_ASSERT
3114 if (FD_ISSET(ms->fd, &ms->fds) == 0) {
3115 MCM_ERRX(MCM_ERR_ASSERT);
3116 return -1;
3117 }
3118 #endif
3119
3120 memcpy(&local_tv, tv, sizeof(struct timeval));
3121
3122 #ifdef DEBUG_MC_PROTO
3123 do {
3124 char *tm;
3125 size_t tml;
3126 tml = asprintf(&tm, "Begin select(2)'ing to see if fd %d is write(2)able. Timeout %llu.%llu", ms->fd, (u_int64_t)local_tv.tv_sec, (u_int64_t)local_tv.tv_usec);
3127 if (tml > 0 && tm != NULL) {
3128 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
3129 free(tm);
3130 }
3131 } while (0);
3132 #endif
3133
3134 /* Before we writev(2) anything, check to make sure the socket is
3135 * ready to accept data to be written out. No sense in wastefully
3136 * calling writev(2) constantly in a loop. */
3137 ret = select(ms->fd + 1, NULL, &ms->fds, NULL, &local_tv);
3138 #ifdef DEBUG_MC_PROTO
3139 do {
3140 char *tm;
3141 size_t tml;
3142 tml = asprintf(&tm, "write(2)able fds: %d/%d", ret, FD_SETSIZE);
3143 if (tml > 0 && tm != NULL) {
3144 MCM_WARNX_MSG(MCM_ERR_TRACE, tm);
3145 free(tm);
3146 }
3147 } while (0);
3148 #endif
3149 if (ret > 0) {
3150 /* Check where writable flag is set */
3151 if (!FD_ISSET(ms->fd, &ms->fds)) {
3152 MCM_ERR(MCM_ERR_SYS_SELECT);
3153 return 0;
3154 }
3155
3156 /* Check for the socket errors */
3157 so_err_length = sizeof(so_err);
3158 if (getsockopt(ms->fd, SOL_SOCKET, SO_ERROR, (void *)&so_err, &so_err_length) == -1) {
3159 MCM_ERR(MCM_ERR_SYS_SELECT); /* Socket error */
3160 return 0;
3161 } else {
3162 return ret;
3163 }
3164 } else if (ret == -1) {
3165 switch (errno) {
3166 case EINTR: /* retry this checking again */
3167 goto retry_check_writable;
3168 default:
3169 MCM_ERR(MCM_ERR_SYS_SELECT);
3170 return 0;
3171 }
3172 } else if (ret == 0) {
3173 MCM_ERR_MSG(MCM_ERR_TIMEOUT, "write select(2) call timed out");
3174 return 0;
3175 }
3176
3177 #ifdef DEBUG_MC_PROTO
3178 MCM_WARNX_MSG(MCM_ERR_TRACE, "End select(2)'ing to see if fd is write(2)able");
3179 #endif
3180
3181 return ret;
3182 #endif
3183 }
3184
3185
3186 int
mcm_set(struct memcache_ctxt * ctxt,struct memcache * mc,char * key,const size_t key_len,const void * val,const size_t bytes,const time_t expire,const u_int16_t flags)3187 mcm_set(struct memcache_ctxt *ctxt, struct memcache *mc,
3188 char *key, const size_t key_len,
3189 const void *val, const size_t bytes,
3190 const time_t expire, const u_int16_t flags) {
3191 return mcm_storage_cmd(ctxt, mc, str_set_cmd, str_set_len, key, key_len, val, bytes, expire, flags);
3192 }
3193
3194
3195 struct memcache_server_stats *
mcm_stats(struct memcache_ctxt * ctxt,struct memcache * mc)3196 mcm_stats(struct memcache_ctxt *ctxt, struct memcache *mc) {
3197 struct memcache_server *ms;
3198 struct memcache_server_stats *s, *ts;
3199
3200 s = mcm_server_stats_new(ctxt);
3201 for (ms = mc->server_list.tqh_first; ms != NULL; ms = ms->entries.tqe_next) {
3202 ts = mcm_server_stats(ctxt, mc, ms);
3203 if (ts == NULL)
3204 continue;
3205
3206 /* Merge the values from ts into s. Any per-server specific data
3207 * is pulled from the last server. */
3208 s->pid = ts->pid;
3209 s->uptime = ts->uptime;
3210 s->time = ts->time;
3211 if (s->version == NULL && ts->version != NULL)
3212 s->version = mcm_strdup(ctxt, ts->version);
3213
3214 s->rusage_user.tv_sec += ts->rusage_user.tv_sec;
3215 s->rusage_user.tv_usec += ts->rusage_user.tv_usec;
3216 if (s->rusage_user.tv_usec > 1000000) {
3217 s->rusage_user.tv_sec += s->rusage_user.tv_usec / 1000000;
3218 s->rusage_user.tv_usec -= 1000000 * (s->rusage_user.tv_usec / 1000000);
3219 }
3220
3221 s->rusage_system.tv_sec += ts->rusage_system.tv_sec;
3222 s->rusage_system.tv_usec += ts->rusage_system.tv_usec;
3223 if (s->rusage_system.tv_usec > 1000000) {
3224 s->rusage_system.tv_sec += s->rusage_system.tv_usec / 1000000;
3225 s->rusage_system.tv_usec -= 1000000 * (s->rusage_system.tv_usec / 1000000);
3226 }
3227
3228 s->curr_items += ts->curr_items;
3229 s->total_items += ts->total_items;
3230 s->bytes = s->bytes + ts->bytes;
3231 s->curr_connections += ts->curr_connections;
3232 s->total_connections += ts->total_connections;
3233 s->connection_structures += ts->connection_structures;
3234 s->cmd_get += ts->cmd_get;
3235 #ifdef SEAN_HACKS
3236 s->cmd_refresh += ts->cmd_refresh;
3237 #endif
3238 s->cmd_set += ts->cmd_set;
3239 s->get_hits += ts->get_hits;
3240 s->get_misses += ts->get_misses;
3241 #ifdef SEAN_HACKS
3242 s->refresh_hits += ts->refresh_hits;
3243 s->refresh_misses += ts->refresh_misses;
3244 #endif
3245 s->bytes_read += ts->bytes_read;
3246 s->bytes_written += ts->bytes_written;
3247 s->limit_maxbytes += ts->limit_maxbytes;
3248
3249 mcm_server_stats_free(ctxt, ts);
3250 }
3251
3252 return s;
3253 }
3254
3255
3256 static int
mcm_storage_cmd(struct memcache_ctxt * ctxt,struct memcache * mc,const char * cmd,const size_t cmd_len,char * key,const size_t key_len,const void * val,const size_t bytes,const time_t expire,const u_int16_t flags)3257 mcm_storage_cmd(struct memcache_ctxt *ctxt, struct memcache *mc,
3258 const char *cmd, const size_t cmd_len,
3259 char *key, const size_t key_len,
3260 const void *val, const size_t bytes,
3261 const time_t expire, const u_int16_t flags) {
3262 char numbuf[11]; /* 10 == (2 ** 32).to_s.length + '\0'.length */
3263 struct memcache_server *ms;
3264 u_int32_t hash;
3265 size_t i;
3266 char *cp;
3267
3268 MCM_VALIDATE_KEY(key, key_len);
3269
3270 /* Reset ctxt->errnum upon entry into memcache(3). */
3271 ctxt->errnum = 0;
3272
3273 hash = ctxt->mcHashKey(ctxt, mc, key, key_len);
3274
3275 ms = mcm_server_connect_next_avail(ctxt, mc, hash);
3276 if (ms == NULL)
3277 return -1;
3278
3279 mcm_buf_append(ctxt, ms->wbuf, cmd, cmd_len);
3280 mcm_buf_append(ctxt, ms->wbuf, key, key_len);
3281 mcm_buf_append_char(ctxt, ms->wbuf, ' ');
3282
3283 /* Convert the value to a string */
3284 i = (size_t)snprintf(numbuf, sizeof(numbuf), "%u", (u_int32_t)flags);
3285 if (i < 1) {
3286 MCM_ERR(MCM_ERR_LIB_SNPRINTF);
3287 MCM_CLEAN_BUFS(ctxt, ms);
3288 return (int)MCM_RET_CODE(-3);
3289 }
3290
3291 mcm_buf_append(ctxt, ms->wbuf, numbuf, i);
3292 mcm_buf_append_char(ctxt, ms->wbuf, ' ');
3293
3294 /* Convert the value to a string */
3295 i = (size_t)snprintf(numbuf, sizeof(numbuf), "%lu", (long int unsigned)expire);
3296 if (i < 1) {
3297 MCM_ERR(MCM_ERR_LIB_SNPRINTF);
3298 MCM_CLEAN_BUFS(ctxt, ms);
3299 return (int)MCM_RET_CODE(-5);
3300 }
3301
3302 mcm_buf_append(ctxt, ms->wbuf, numbuf, i);
3303 mcm_buf_append_char(ctxt, ms->wbuf, ' ');
3304
3305 /* Convert the value to a string */
3306 i = (size_t)snprintf(numbuf, sizeof(numbuf), "%lu", (long int unsigned)bytes);
3307 if (i < 1) {
3308 MCM_ERR(MCM_ERR_LIB_SNPRINTF);
3309 MCM_CLEAN_BUFS(ctxt, ms);
3310 return (int)MCM_RET_CODE(-7);
3311 }
3312
3313 mcm_buf_append(ctxt, ms->wbuf, numbuf, i);
3314 mcm_buf_append(ctxt, ms->wbuf, str_endl, str_endl_len);
3315
3316 /* Add the data */
3317 mcm_buf_append(ctxt, ms->wbuf, val, bytes);
3318
3319 /* Add another carriage return */
3320 mcm_buf_append(ctxt, ms->wbuf, str_endl, str_endl_len);
3321
3322 if (mcm_server_send_cmd(ctxt, mc, ms) < 0) {
3323 MCM_CLEAN_BUFS(ctxt, ms);
3324 return (int)MCM_RET_CODE(-8);
3325 }
3326
3327 cp = mcm_get_line(ctxt, mc, ms);
3328 if (cp != NULL && memcmp(cp, "STORED", MCM_CSTRLEN("STORED")) == 0) {
3329 /* Groovy Tuesday */
3330 MCM_CLEAN_BUFS(ctxt, ms);
3331 return 0;
3332 } else if (cp != NULL && memcmp(cp, "NOT_STORED", MCM_CSTRLEN("NOT_STORED")) == 0) {
3333 /* Fuck beans. That was them, wasn't it? */
3334 MCM_ERR_MSG(MCM_ERR_MC_STORE, cmd);
3335 MCM_CLEAN_BUFS(ctxt, ms);
3336 return (int)MCM_RET_CODE(1);
3337 } else if (cp != NULL && memcmp(cp, "SERVER_ERROR ", MCM_CSTRLEN("SERVER_ERROR ")) == 0) {
3338 /* Drat! Not enough memory on the server for this key. */
3339 MCM_ERR_MSG(MCM_ERR_MC_STORE, cp + MCM_CSTRLEN("SERVER_ERROR "));
3340 MCM_CLEAN_BUFS(ctxt, ms);
3341 return (int)MCM_RET_CODE(4);
3342 }
3343
3344 if (mc->num_servers == 0) {
3345 MCM_ERRX(MCM_ERR_MC_SERV_LIST);
3346 MCM_CLEAN_BUFS(ctxt, ms);
3347 return (int)MCM_RET_CODE(3);
3348 } else {
3349 MCM_CLEAN_BUFS(ctxt, ms);
3350 return (int)MCM_RET_CODE(2);
3351 }
3352 }
3353
3354
3355 char *
mcm_strdup(const struct memcache_ctxt * ctxt,const char * str)3356 mcm_strdup(const struct memcache_ctxt *ctxt, const char *str) {
3357 return mcm_strndup(ctxt, str, strlen(str));
3358 }
3359
3360
3361 char *
mcm_strnchr(const struct memcache_ctxt * ctxt,mc_const char * str,int c,const size_t len)3362 mcm_strnchr(const struct memcache_ctxt *ctxt, mc_const char *str, int c, const size_t len) {
3363 char *cp;
3364 size_t i;
3365
3366 for (cp = str, i = 0; i < len && *cp != '\0'; i++, cp++) {
3367 if (c == (int)*cp)
3368 return cp;
3369 }
3370
3371 return '\0';
3372 }
3373
3374
3375 char *
mcm_strndup(const struct memcache_ctxt * ctxt,const char * str,const size_t len)3376 mcm_strndup(const struct memcache_ctxt *ctxt, const char *str, const size_t len) {
3377 char *cp;
3378
3379 cp = ctxt->mcMallocAtomic(len + MCM_CSTRLEN("\0"));
3380 if (cp != NULL) {
3381 memcpy(cp, str, len);
3382 cp[len] = '\0';
3383 }
3384
3385 return cp;
3386 }
3387
3388
3389 /*
3390 * Find the first occurrence of find in s, where the search is limited to the
3391 * first slen characters of s.
3392 */
3393 char *
mcm_strnstr(const struct memcache_ctxt * ctxt,mc_const char * s,const char * find,size_t slen)3394 mcm_strnstr(const struct memcache_ctxt *ctxt, mc_const char *s, const char *find, size_t slen) {
3395 char c, sc;
3396 size_t len;
3397
3398 if ((c = *find++) != '\0') {
3399 len = strlen(find);
3400 do {
3401 do {
3402 if (slen-- < 1 || (sc = *s++) == '\0')
3403 return (NULL);
3404 } while (sc != c);
3405 if (len > slen)
3406 return (NULL);
3407 } while (strncmp(s, find, len) != 0);
3408 s--;
3409 }
3410 return ((char *)s);
3411 }
3412
3413
3414 void
mcm_timeout(const struct memcache_ctxt * ctxt,struct memcache * mc,const int sec,const int msec)3415 mcm_timeout(const struct memcache_ctxt *ctxt, struct memcache *mc, const int sec, const int msec) {
3416 mc->tv.tv_sec = sec;
3417 mc->tv.tv_usec = msec;
3418 }
3419
3420
3421 inline static int32_t
mcm_validate_key(const struct memcache_ctxt * ctxt,char * key,size_t len)3422 mcm_validate_key(const struct memcache_ctxt *ctxt, char *key, size_t len) {
3423 return (ctxt->mcKeyValid != NULL ? ctxt->mcKeyValid(ctxt, key, len) : 0);
3424 }
3425
3426
3427 static int32_t
mcm_validate_key_func(MCM_KEY_VALID_FUNC_ARGS)3428 mcm_validate_key_func(MCM_KEY_VALID_FUNC_ARGS) {
3429 const struct memcache_ctxt *ctxt;
3430 char *cp, *key;
3431 size_t len;
3432 size_t i;
3433
3434 MCM_KEY_VALID_INIT(ctxt, key, len);
3435
3436 for (i = 0, cp = key; i < len; i++, cp++) {
3437 if (isspace(*cp)) {
3438 MCM_ERRX_MSG_LVL(MCM_ERR_PROTO, "isspace(3) returned true for character in key", MCM_ERR_LVL_ERR);
3439 return (int32_t)MCM_RET_CODE((int32_t)(i + 1));
3440 }
3441 }
3442
3443 return 0;
3444 }
3445
3446
3447 u_int32_t
mcm_vernum(const struct memcache_ctxt * ctxt)3448 mcm_vernum(const struct memcache_ctxt *ctxt) {
3449 return MEMCACHE_VERNUM;
3450 }
3451
3452
3453 const char *
mcm_version(const struct memcache_ctxt * ctxt)3454 mcm_version(const struct memcache_ctxt *ctxt) {
3455 return MEMCACHE_VER;
3456 }
3457
3458
3459 /* BEGIN memcache memory API */
3460 int
mcErrGet(mcErrFunc * errFunc)3461 mcErrGet(mcErrFunc *errFunc) {
3462 if (errFunc != NULL)
3463 *errFunc = mcGlobalCtxt.mcErr;
3464
3465 return 0;
3466 }
3467
3468
3469 int
mcErrSetup(mcErrFunc errFunc)3470 mcErrSetup(mcErrFunc errFunc) {
3471 return mcErrSetupCtxt(&mcGlobalCtxt, errFunc);
3472 }
3473
3474
3475 int
mcErrSetupCtxt(struct memcache_ctxt * ctxt,mcErrFunc errFunc)3476 mcErrSetupCtxt(struct memcache_ctxt *ctxt, mcErrFunc errFunc) {
3477 if (ctxt == NULL || errFunc == NULL)
3478 return 1;
3479
3480 ctxt->mcErr = errFunc;
3481
3482 return 0;
3483 }
3484
3485
3486 int
mcMemGet(mcFreeFunc * freeFunc,mcMallocFunc * mallocFunc,mcMallocFunc * mallocAtomicFunc,mcReallocFunc * reallocFunc)3487 mcMemGet(mcFreeFunc *freeFunc, mcMallocFunc *mallocFunc, mcMallocFunc *mallocAtomicFunc,
3488 mcReallocFunc *reallocFunc) {
3489 if (freeFunc != NULL)
3490 *freeFunc = mcGlobalCtxt.mcFree;
3491
3492 if (mallocFunc != NULL)
3493 *mallocFunc = mcGlobalCtxt.mcMalloc;
3494
3495 if (mallocAtomicFunc != NULL)
3496 *mallocAtomicFunc = mcGlobalCtxt.mcMallocAtomic;
3497
3498 if (reallocFunc != NULL)
3499 *reallocFunc = mcGlobalCtxt.mcRealloc;
3500
3501 return 0;
3502 }
3503
3504
3505 struct memcache_ctxt *
mcMemNewCtxt(mcFreeFunc freeFunc,mcMallocFunc mallocFunc,mcMallocFunc mallocAtomicFunc,mcReallocFunc reallocFunc)3506 mcMemNewCtxt(mcFreeFunc freeFunc, mcMallocFunc mallocFunc, mcMallocFunc mallocAtomicFunc,
3507 mcReallocFunc reallocFunc) {
3508 struct memcache_ctxt *ctxt;
3509
3510 if (freeFunc == NULL || mallocFunc == NULL || reallocFunc == NULL)
3511 return NULL;
3512
3513 ctxt = mallocFunc(sizeof(struct memcache_ctxt));
3514 if (ctxt != NULL) {
3515 bzero(ctxt, sizeof(struct memcache_ctxt));
3516
3517 ctxt->ectxt = mallocFunc(sizeof(struct memcache_err_ctxt));
3518 if (ctxt->ectxt == NULL) {
3519 freeFunc(ctxt);
3520 return NULL;
3521 }
3522 bzero(ctxt->ectxt, sizeof(struct memcache_err_ctxt));
3523
3524 if (mcMemSetupCtxt(ctxt, freeFunc, mallocFunc, mallocAtomicFunc, reallocFunc) != 0) {
3525 bzero(ctxt, sizeof(struct memcache_ctxt));
3526 freeFunc(ctxt->ectxt);
3527 freeFunc(ctxt);
3528 return NULL;
3529 }
3530
3531 /* Install our default error handler */
3532 ctxt->mcErr = mcm_err_func;
3533 ctxt->mcKeyValid = mcm_validate_key_func;
3534 ctxt->mcHashKey = mcm_hash_key_func;
3535 ctxt->mcServerFind = mcm_server_find_func;
3536
3537 /* By default, ignore INFO and NOTICE level messages */
3538 ctxt->MCM_ERR_MASK = MCM_ERR_LVL_INFO | MCM_ERR_LVL_NOTICE;
3539 }
3540 return ctxt;
3541 }
3542
3543
3544 void
mcMemFreeCtxt(struct memcache_ctxt * ctxt)3545 mcMemFreeCtxt(struct memcache_ctxt *ctxt) {
3546 mcFreeFunc freeFunc;
3547
3548 if (ctxt == NULL || ctxt->mcFree == NULL)
3549 return;
3550
3551 freeFunc = ctxt->mcFree;
3552 freeFunc(ctxt->ectxt);
3553 freeFunc(ctxt);
3554 }
3555
3556
3557 int
mcMemSetup(mcFreeFunc freeFunc,mcMallocFunc mallocFunc,mcMallocFunc mallocAtomicFunc,mcReallocFunc reallocFunc)3558 mcMemSetup(mcFreeFunc freeFunc, mcMallocFunc mallocFunc,
3559 mcMallocFunc mallocAtomicFunc, mcReallocFunc reallocFunc) {
3560 return mcMemSetupCtxt(&mcGlobalCtxt, freeFunc, mallocFunc, mallocAtomicFunc, reallocFunc);
3561 }
3562
3563
3564 int
mcMemSetupCtxt(struct memcache_ctxt * ctxt,mcFreeFunc freeFunc,mcMallocFunc mallocFunc,mcMallocFunc mallocAtomicFunc,mcReallocFunc reallocFunc)3565 mcMemSetupCtxt(struct memcache_ctxt *ctxt, mcFreeFunc freeFunc, mcMallocFunc mallocFunc,
3566 mcMallocFunc mallocAtomicFunc, mcReallocFunc reallocFunc) {
3567 if (ctxt == NULL || freeFunc == NULL || mallocFunc == NULL || reallocFunc == NULL)
3568 return(1);
3569
3570 ctxt->mcFree = freeFunc;
3571 ctxt->mcMalloc = mallocFunc;
3572 ctxt->mcMallocAtomic = (mallocAtomicFunc != NULL ? mallocAtomicFunc : mallocFunc);
3573 ctxt->mcRealloc = reallocFunc;
3574
3575 return 0;
3576 }
3577 /* END memcache memory API */
3578