"Fossies" - the Fresh Open Source Software Archive 
Member "memcached-1.6.15/proto_text.c" (30 Mar 2022, 95612 Bytes) of package /linux/www/memcached-1.6.15.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "proto_text.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.6.14_vs_1.6.15.
1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Functions for handling the text related protocols, original and meta.
4 */
5
6 #include "memcached.h"
7 #include "proto_text.h"
8 // FIXME: only for process_proxy_stats()
9 // - some better/different structure for stats subcommands
10 // would remove this abstraction leak.
11 #include "proto_proxy.h"
12 #include "authfile.h"
13 #include "storage.h"
14 #include "base64.h"
15 #ifdef TLS
16 #include "tls.h"
17 #endif
18 #include <string.h>
19 #include <stdlib.h>
20
21 #define META_SPACE(p) { \
22 *p = ' '; \
23 p++; \
24 }
25
26 #define META_CHAR(p, c) { \
27 *p = ' '; \
28 *(p+1) = c; \
29 p += 2; \
30 }
31
32 // NOTE: being a little casual with the write buffer.
33 // the buffer needs to be sized that the longest possible meta response will
34 // fit. Here we allow the key to fill up to half the write buffer, in case
35 // something terrible has gone wrong.
36 #define META_KEY(p, key, nkey, bin) { \
37 META_CHAR(p, 'k'); \
38 if (!bin) { \
39 memcpy(p, key, nkey); \
40 p += nkey; \
41 } else { \
42 p += base64_encode((unsigned char *) key, nkey, (unsigned char *)p, WRITE_BUFFER_SIZE / 2); \
43 *p = ' '; \
44 *(p+1) = 'b'; \
45 p += 2; \
46 } \
47 }
48
49 typedef struct token_s {
50 char *value;
51 size_t length;
52 } token_t;
53
54 static void _finalize_mset(conn *c, enum store_item_type ret) {
55 mc_resp *resp = c->resp;
56 item *it = c->item;
57 conn_set_state(c, conn_new_cmd);
58
59 // information about the response line has been stashed in wbuf.
60 char *p = resp->wbuf + resp->wbytes;
61 char *end = p; // end of the stashed data portion.
62
63 switch (ret) {
64 case STORED:
65 if (settings.meta_response_old) {
66 memcpy(p, "OK", 2);
67 } else {
68 memcpy(p, "HD", 2);
69 }
70 // Only place noreply is used for meta cmds is a nominal response.
71 if (c->noreply) {
72 resp->skip = true;
73 }
74 break;
75 case EXISTS:
76 memcpy(p, "EX", 2);
77 break;
78 case NOT_FOUND:
79 memcpy(p, "NF", 2);
80 break;
81 case NOT_STORED:
82 memcpy(p, "NS", 2);
83 break;
84 default:
85 c->noreply = false;
86 out_string(c, "SERVER_ERROR Unhandled storage type.");
87 return;
88 }
89 p += 2;
90
91 for (char *fp = resp->wbuf; fp < end; fp++) {
92 switch (*fp) {
93 case 'O':
94 // Copy stashed opaque.
95 META_SPACE(p);
96 while (fp < end && *fp != ' ') {
97 *p = *fp;
98 p++;
99 fp++;
100 }
101 break;
102 case 'k':
103 // Encode the key here instead of earlier to minimize copying.
104 META_KEY(p, ITEM_key(it), it->nkey, (it->it_flags & ITEM_KEY_BINARY));
105 break;
106 case 'c':
107 // We don't have the CAS until this point, which is why we
108 // generate this line so late.
109 META_CHAR(p, 'c');
110 p = itoa_u64(c->cas, p);
111 break;
112 default:
113 break;
114 }
115 }
116
117 memcpy(p, "\r\n", 2);
118 p += 2;
119 // we're offset into wbuf, but good convention to track wbytes.
120 resp->wbytes = p - resp->wbuf;
121 resp_add_iov(resp, end, p - end);
122 }
123
124 /*
125 * we get here after reading the value in set/add/replace commands. The command
126 * has been stored in c->cmd, and the item is ready in c->item.
127 */
128 void complete_nread_ascii(conn *c) {
129 assert(c != NULL);
130
131 item *it = c->item;
132 int comm = c->cmd;
133 enum store_item_type ret;
134 bool is_valid = false;
135
136 pthread_mutex_lock(&c->thread->stats.mutex);
137 c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++;
138 pthread_mutex_unlock(&c->thread->stats.mutex);
139
140 if ((it->it_flags & ITEM_CHUNKED) == 0) {
141 if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) == 0) {
142 is_valid = true;
143 }
144 } else {
145 char buf[2];
146 /* should point to the final item chunk */
147 item_chunk *ch = (item_chunk *) c->ritem;
148 assert(ch->used != 0);
149 /* :( We need to look at the last two bytes. This could span two
150 * chunks.
151 */
152 if (ch->used > 1) {
153 buf[0] = ch->data[ch->used - 2];
154 buf[1] = ch->data[ch->used - 1];
155 } else {
156 assert(ch->prev);
157 assert(ch->used == 1);
158 buf[0] = ch->prev->data[ch->prev->used - 1];
159 buf[1] = ch->data[ch->used - 1];
160 }
161 if (strncmp(buf, "\r\n", 2) == 0) {
162 is_valid = true;
163 } else {
164 assert(1 == 0);
165 }
166 }
167
168 if (!is_valid) {
169 // metaset mode always returns errors.
170 if (c->mset_res) {
171 c->noreply = false;
172 }
173 out_string(c, "CLIENT_ERROR bad data chunk");
174 } else {
175 ret = store_item(it, comm, c);
176
177 #ifdef ENABLE_DTRACE
178 uint64_t cas = ITEM_get_cas(it);
179 switch (c->cmd) {
180 case NREAD_ADD:
181 MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
182 (ret == 1) ? it->nbytes : -1, cas);
183 break;
184 case NREAD_REPLACE:
185 MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
186 (ret == 1) ? it->nbytes : -1, cas);
187 break;
188 case NREAD_APPEND:
189 MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
190 (ret == 1) ? it->nbytes : -1, cas);
191 break;
192 case NREAD_PREPEND:
193 MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
194 (ret == 1) ? it->nbytes : -1, cas);
195 break;
196 case NREAD_SET:
197 MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
198 (ret == 1) ? it->nbytes : -1, cas);
199 break;
200 case NREAD_CAS:
201 MEMCACHED_COMMAND_CAS(c->sfd, ITEM_key(it), it->nkey, it->nbytes,
202 cas);
203 break;
204 }
205 #endif
206
207 if (c->mset_res) {
208 _finalize_mset(c, ret);
209 } else {
210 switch (ret) {
211 case STORED:
212 out_string(c, "STORED");
213 break;
214 case EXISTS:
215 out_string(c, "EXISTS");
216 break;
217 case NOT_FOUND:
218 out_string(c, "NOT_FOUND");
219 break;
220 case NOT_STORED:
221 out_string(c, "NOT_STORED");
222 break;
223 default:
224 out_string(c, "SERVER_ERROR Unhandled storage type.");
225 }
226 }
227
228 }
229
230 c->set_stale = false; /* force flag to be off just in case */
231 c->mset_res = false;
232 item_remove(c->item); /* release the c->item reference */
233 c->item = 0;
234 }
235
236 #define COMMAND_TOKEN 0
237 #define SUBCOMMAND_TOKEN 1
238 #define KEY_TOKEN 1
239
240 #define MAX_TOKENS 24
241
242 #define WANT_TOKENS(ntokens, min, max) \
243 do { \
244 if ((min != -1 && ntokens < min) || (max != -1 && ntokens > max)) { \
245 out_string(c, "ERROR"); \
246 return; \
247 } \
248 } while (0)
249
250 #define WANT_TOKENS_OR(ntokens, a, b) \
251 do { \
252 if (ntokens != a && ntokens != b) { \
253 out_string(c, "ERROR"); \
254 return; \
255 } \
256 } while (0)
257
258 #define WANT_TOKENS_MIN(ntokens, min) \
259 do { \
260 if (ntokens < min) { \
261 out_string(c, "ERROR"); \
262 return; \
263 } \
264 } while (0)
265
266 /*
267 * Tokenize the command string by replacing whitespace with '\0' and update
268 * the token array tokens with pointer to start of each token and length.
269 * Returns total number of tokens. The last valid token is the terminal
270 * token (value points to the first unprocessed character of the string and
271 * length zero).
272 *
273 * Usage example:
274 *
275 * while(tokenize_command(command, ncommand, tokens, max_tokens) > 0) {
276 * for(int ix = 0; tokens[ix].length != 0; ix++) {
277 * ...
278 * }
279 * ncommand = tokens[ix].value - command;
280 * command = tokens[ix].value;
281 * }
282 */
283 static size_t tokenize_command(char *command, token_t *tokens, const size_t max_tokens) {
284 char *s, *e;
285 size_t ntokens = 0;
286 assert(command != NULL && tokens != NULL && max_tokens > 1);
287 size_t len = strlen(command);
288 unsigned int i = 0;
289
290 s = e = command;
291 for (i = 0; i < len; i++) {
292 if (*e == ' ') {
293 if (s != e) {
294 tokens[ntokens].value = s;
295 tokens[ntokens].length = e - s;
296 ntokens++;
297 *e = '\0';
298 if (ntokens == max_tokens - 1) {
299 e++;
300 s = e; /* so we don't add an extra token */
301 break;
302 }
303 }
304 s = e + 1;
305 }
306 e++;
307 }
308
309 if (s != e) {
310 tokens[ntokens].value = s;
311 tokens[ntokens].length = e - s;
312 ntokens++;
313 }
314
315 /*
316 * If we scanned the whole string, the terminal value pointer is null,
317 * otherwise it is the first unprocessed character.
318 */
319 tokens[ntokens].value = *e == '\0' ? NULL : e;
320 tokens[ntokens].length = 0;
321 ntokens++;
322
323 return ntokens;
324 }
325
326 int try_read_command_asciiauth(conn *c) {
327 token_t tokens[MAX_TOKENS];
328 size_t ntokens;
329 char *cont = NULL;
330
331 // TODO: move to another function.
332 if (!c->sasl_started) {
333 char *el;
334 uint32_t size = 0;
335
336 // impossible for the auth command to be this short.
337 if (c->rbytes < 2)
338 return 0;
339
340 el = memchr(c->rcurr, '\n', c->rbytes);
341
342 // If no newline after 1k, getting junk data, close out.
343 if (!el) {
344 if (c->rbytes > 2048) {
345 conn_set_state(c, conn_closing);
346 return 1;
347 }
348 return 0;
349 }
350
351 // Looking for: "set foo 0 0 N\r\nuser pass\r\n"
352 // key, flags, and ttl are ignored. N is used to see if we have the rest.
353
354 // so tokenize doesn't walk past into the value.
355 // it's fine to leave the \r in, as strtoul will stop at it.
356 *el = '\0';
357
358 ntokens = tokenize_command(c->rcurr, tokens, MAX_TOKENS);
359 // ensure the buffer is consumed.
360 c->rbytes -= (el - c->rcurr) + 1;
361 c->rcurr += (el - c->rcurr) + 1;
362
363 // final token is a NULL ender, so we have one more than expected.
364 if (ntokens < 6
365 || strcmp(tokens[0].value, "set") != 0
366 || !safe_strtoul(tokens[4].value, &size)) {
367 if (!c->resp) {
368 if (!resp_start(c)) {
369 conn_set_state(c, conn_closing);
370 return 1;
371 }
372 }
373 out_string(c, "CLIENT_ERROR unauthenticated");
374 return 1;
375 }
376
377 // we don't actually care about the key at all; it can be anything.
378 // we do care about the size of the remaining read.
379 c->rlbytes = size + 2;
380
381 c->sasl_started = true; // reuse from binprot sasl, but not sasl :)
382 }
383
384 if (c->rbytes < c->rlbytes) {
385 // need more bytes.
386 return 0;
387 }
388
389 // Going to respond at this point, so attach a response object.
390 if (!c->resp) {
391 if (!resp_start(c)) {
392 conn_set_state(c, conn_closing);
393 return 1;
394 }
395 }
396
397 cont = c->rcurr;
398 // advance buffer. no matter what we're stopping.
399 c->rbytes -= c->rlbytes;
400 c->rcurr += c->rlbytes;
401 c->sasl_started = false;
402
403 // must end with \r\n
404 // NB: I thought ASCII sets also worked with just \n, but according to
405 // complete_nread_ascii only \r\n is valid.
406 if (strncmp(cont + c->rlbytes - 2, "\r\n", 2) != 0) {
407 out_string(c, "CLIENT_ERROR bad command line termination");
408 return 1;
409 }
410
411 // payload should be "user pass", so we can use the tokenizer.
412 cont[c->rlbytes - 2] = '\0';
413 ntokens = tokenize_command(cont, tokens, MAX_TOKENS);
414
415 if (ntokens < 3) {
416 out_string(c, "CLIENT_ERROR bad authentication token format");
417 return 1;
418 }
419
420 if (authfile_check(tokens[0].value, tokens[1].value) == 1) {
421 out_string(c, "STORED");
422 c->authenticated = true;
423 c->try_read_command = try_read_command_ascii;
424 pthread_mutex_lock(&c->thread->stats.mutex);
425 c->thread->stats.auth_cmds++;
426 pthread_mutex_unlock(&c->thread->stats.mutex);
427 } else {
428 out_string(c, "CLIENT_ERROR authentication failure");
429 pthread_mutex_lock(&c->thread->stats.mutex);
430 c->thread->stats.auth_cmds++;
431 c->thread->stats.auth_errors++;
432 pthread_mutex_unlock(&c->thread->stats.mutex);
433 }
434
435 return 1;
436 }
437
438 int try_read_command_ascii(conn *c) {
439 char *el, *cont;
440
441 if (c->rbytes == 0)
442 return 0;
443
444 el = memchr(c->rcurr, '\n', c->rbytes);
445 if (!el) {
446 if (c->rbytes > 2048) {
447 /*
448 * We didn't have a '\n' in the first few k. This _has_ to be a
449 * large multiget, if not we should just nuke the connection.
450 */
451 char *ptr = c->rcurr;
452 while (*ptr == ' ') { /* ignore leading whitespaces */
453 ++ptr;
454 }
455
456 if (ptr - c->rcurr > 100 ||
457 (strncmp(ptr, "get ", 4) && strncmp(ptr, "gets ", 5))) {
458
459 conn_set_state(c, conn_closing);
460 return 1;
461 }
462
463 // ASCII multigets are unbound, so our fixed size rbuf may not
464 // work for this particular workload... For backcompat we'll use a
465 // malloc/realloc/free routine just for this.
466 if (!c->rbuf_malloced) {
467 if (!rbuf_switch_to_malloc(c)) {
468 conn_set_state(c, conn_closing);
469 return 1;
470 }
471 }
472 }
473
474 return 0;
475 }
476 cont = el + 1;
477 if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {
478 el--;
479 }
480 *el = '\0';
481
482 assert(cont <= (c->rcurr + c->rbytes));
483
484 c->last_cmd_time = current_time;
485 process_command_ascii(c, c->rcurr);
486
487 c->rbytes -= (cont - c->rcurr);
488 c->rcurr = cont;
489
490 assert(c->rcurr <= (c->rbuf + c->rsize));
491
492 return 1;
493 }
494
495
496 static inline bool set_noreply_maybe(conn *c, token_t *tokens, size_t ntokens)
497 {
498 int noreply_index = ntokens - 2;
499
500 /*
501 NOTE: this function is not the first place where we are going to
502 send the reply. We could send it instead from process_command()
503 if the request line has wrong number of tokens. However parsing
504 malformed line for "noreply" option is not reliable anyway, so
505 it can't be helped.
506 */
507 if (tokens[noreply_index].value
508 && strcmp(tokens[noreply_index].value, "noreply") == 0) {
509 c->noreply = true;
510 }
511 return c->noreply;
512 }
513
514 /* client flags == 0 means use no storage for client flags */
515 static inline int make_ascii_get_suffix(char *suffix, item *it, bool return_cas, int nbytes) {
516 char *p = suffix;
517 *p = ' ';
518 p++;
519 if (FLAGS_SIZE(it) == 0) {
520 *p = '0';
521 p++;
522 } else {
523 p = itoa_u32(*((uint32_t *) ITEM_suffix(it)), p);
524 }
525 *p = ' ';
526 p = itoa_u32(nbytes-2, p+1);
527
528 if (return_cas) {
529 *p = ' ';
530 p = itoa_u64(ITEM_get_cas(it), p+1);
531 }
532
533 *p = '\r';
534 *(p+1) = '\n';
535 *(p+2) = '\0';
536 return (p - suffix) + 2;
537 }
538
539 /* ntokens is overwritten here... shrug.. */
540 static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas, bool should_touch) {
541 char *key;
542 size_t nkey;
543 item *it;
544 token_t *key_token = &tokens[KEY_TOKEN];
545 int32_t exptime_int = 0;
546 rel_time_t exptime = 0;
547 bool fail_length = false;
548 assert(c != NULL);
549 mc_resp *resp = c->resp;
550
551 if (should_touch) {
552 // For get and touch commands, use first token as exptime
553 if (!safe_strtol(tokens[1].value, &exptime_int)) {
554 out_string(c, "CLIENT_ERROR invalid exptime argument");
555 return;
556 }
557 key_token++;
558 exptime = realtime(EXPTIME_TO_POSITIVE_TIME(exptime_int));
559 }
560
561 do {
562 while(key_token->length != 0) {
563 bool overflow; // not used here.
564 key = key_token->value;
565 nkey = key_token->length;
566
567 if (nkey > KEY_MAX_LENGTH) {
568 fail_length = true;
569 goto stop;
570 }
571
572 it = limited_get(key, nkey, c, exptime, should_touch, DO_UPDATE, &overflow);
573 if (settings.detail_enabled) {
574 stats_prefix_record_get(key, nkey, NULL != it);
575 }
576 if (it) {
577 /*
578 * Construct the response. Each hit adds three elements to the
579 * outgoing data list:
580 * "VALUE "
581 * key
582 * " " + flags + " " + data length + "\r\n" + data (with \r\n)
583 */
584
585 {
586 MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
587 it->nbytes, ITEM_get_cas(it));
588 int nbytes = it->nbytes;;
589 nbytes = it->nbytes;
590 char *p = resp->wbuf;
591 memcpy(p, "VALUE ", 6);
592 p += 6;
593 memcpy(p, ITEM_key(it), it->nkey);
594 p += it->nkey;
595 p += make_ascii_get_suffix(p, it, return_cas, nbytes);
596 resp_add_iov(resp, resp->wbuf, p - resp->wbuf);
597
598 #ifdef EXTSTORE
599 if (it->it_flags & ITEM_HDR) {
600 if (storage_get_item(c, it, resp) != 0) {
601 pthread_mutex_lock(&c->thread->stats.mutex);
602 c->thread->stats.get_oom_extstore++;
603 pthread_mutex_unlock(&c->thread->stats.mutex);
604
605 item_remove(it);
606 goto stop;
607 }
608 } else if ((it->it_flags & ITEM_CHUNKED) == 0) {
609 resp_add_iov(resp, ITEM_data(it), it->nbytes);
610 } else {
611 resp_add_chunked_iov(resp, it, it->nbytes);
612 }
613 #else
614 if ((it->it_flags & ITEM_CHUNKED) == 0) {
615 resp_add_iov(resp, ITEM_data(it), it->nbytes);
616 } else {
617 resp_add_chunked_iov(resp, it, it->nbytes);
618 }
619 #endif
620 }
621
622 if (settings.verbose > 1) {
623 int ii;
624 fprintf(stderr, ">%d sending key ", c->sfd);
625 for (ii = 0; ii < it->nkey; ++ii) {
626 fprintf(stderr, "%c", key[ii]);
627 }
628 fprintf(stderr, "\n");
629 }
630
631 /* item_get() has incremented it->refcount for us */
632 pthread_mutex_lock(&c->thread->stats.mutex);
633 if (should_touch) {
634 c->thread->stats.touch_cmds++;
635 c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
636 } else {
637 c->thread->stats.lru_hits[it->slabs_clsid]++;
638 c->thread->stats.get_cmds++;
639 }
640 pthread_mutex_unlock(&c->thread->stats.mutex);
641 #ifdef EXTSTORE
642 /* If ITEM_HDR, an io_wrap owns the reference. */
643 if ((it->it_flags & ITEM_HDR) == 0) {
644 resp->item = it;
645 }
646 #else
647 resp->item = it;
648 #endif
649 } else {
650 pthread_mutex_lock(&c->thread->stats.mutex);
651 if (should_touch) {
652 c->thread->stats.touch_cmds++;
653 c->thread->stats.touch_misses++;
654 } else {
655 c->thread->stats.get_misses++;
656 c->thread->stats.get_cmds++;
657 }
658 MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
659 pthread_mutex_unlock(&c->thread->stats.mutex);
660 }
661
662 key_token++;
663 if (key_token->length != 0) {
664 if (!resp_start(c)) {
665 goto stop;
666 }
667 resp = c->resp;
668 }
669 }
670
671 /*
672 * If the command string hasn't been fully processed, get the next set
673 * of tokens.
674 */
675 if (key_token->value != NULL) {
676 ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS);
677 key_token = tokens;
678 if (!resp_start(c)) {
679 goto stop;
680 }
681 resp = c->resp;
682 }
683 } while(key_token->value != NULL);
684 stop:
685
686 if (settings.verbose > 1)
687 fprintf(stderr, ">%d END\n", c->sfd);
688
689 /*
690 If the loop was terminated because of out-of-memory, it is not
691 reliable to add END\r\n to the buffer, because it might not end
692 in \r\n. So we send SERVER_ERROR instead.
693 */
694 if (key_token->value != NULL) {
695 // Kill any stacked responses we had.
696 conn_release_items(c);
697 // Start a new response object for the error message.
698 if (!resp_start(c)) {
699 // severe out of memory error.
700 conn_set_state(c, conn_closing);
701 return;
702 }
703 if (fail_length) {
704 out_string(c, "CLIENT_ERROR bad command line format");
705 } else {
706 out_of_memory(c, "SERVER_ERROR out of memory writing get response");
707 }
708 } else {
709 // Tag the end token onto the most recent response object.
710 resp_add_iov(resp, "END\r\n", 5);
711 conn_set_state(c, conn_mwrite);
712 }
713 }
714
715 inline static void process_stats_detail(conn *c, const char *command) {
716 assert(c != NULL);
717
718 if (strcmp(command, "on") == 0) {
719 settings.detail_enabled = 1;
720 out_string(c, "OK");
721 }
722 else if (strcmp(command, "off") == 0) {
723 settings.detail_enabled = 0;
724 out_string(c, "OK");
725 }
726 else if (strcmp(command, "dump") == 0) {
727 int len;
728 char *stats = stats_prefix_dump(&len);
729 write_and_free(c, stats, len);
730 }
731 else {
732 out_string(c, "CLIENT_ERROR usage: stats detail on|off|dump");
733 }
734 }
735
736 static void process_stat(conn *c, token_t *tokens, const size_t ntokens) {
737 const char *subcommand = tokens[SUBCOMMAND_TOKEN].value;
738 assert(c != NULL);
739
740 if (ntokens < 2) {
741 out_string(c, "CLIENT_ERROR bad command line");
742 return;
743 }
744
745 if (ntokens == 2) {
746 server_stats(&append_stats, c);
747 (void)get_stats(NULL, 0, &append_stats, c);
748 } else if (strcmp(subcommand, "reset") == 0) {
749 stats_reset();
750 out_string(c, "RESET");
751 return;
752 } else if (strcmp(subcommand, "detail") == 0) {
753 /* NOTE: how to tackle detail with binary? */
754 if (ntokens < 4)
755 process_stats_detail(c, ""); /* outputs the error message */
756 else
757 process_stats_detail(c, tokens[2].value);
758 /* Output already generated */
759 return;
760 } else if (strcmp(subcommand, "settings") == 0) {
761 process_stat_settings(&append_stats, c);
762 } else if (strcmp(subcommand, "cachedump") == 0) {
763 char *buf;
764 unsigned int bytes, id, limit = 0;
765
766 if (!settings.dump_enabled) {
767 out_string(c, "CLIENT_ERROR stats cachedump not allowed");
768 return;
769 }
770
771 if (ntokens < 5) {
772 out_string(c, "CLIENT_ERROR bad command line");
773 return;
774 }
775
776 if (!safe_strtoul(tokens[2].value, &id) ||
777 !safe_strtoul(tokens[3].value, &limit)) {
778 out_string(c, "CLIENT_ERROR bad command line format");
779 return;
780 }
781
782 if (id >= MAX_NUMBER_OF_SLAB_CLASSES) {
783 out_string(c, "CLIENT_ERROR Illegal slab id");
784 return;
785 }
786
787 buf = item_cachedump(id, limit, &bytes);
788 write_and_free(c, buf, bytes);
789 return;
790 } else if (strcmp(subcommand, "conns") == 0) {
791 process_stats_conns(&append_stats, c);
792 #ifdef EXTSTORE
793 } else if (strcmp(subcommand, "extstore") == 0) {
794 process_extstore_stats(&append_stats, c);
795 #endif
796 #ifdef PROXY
797 } else if (strcmp(subcommand, "proxy") == 0) {
798 process_proxy_stats(&append_stats, c);
799 #endif
800 } else {
801 /* getting here means that the subcommand is either engine specific or
802 is invalid. query the engine and see. */
803 if (get_stats(subcommand, strlen(subcommand), &append_stats, c)) {
804 if (c->stats.buffer == NULL) {
805 out_of_memory(c, "SERVER_ERROR out of memory writing stats");
806 } else {
807 write_and_free(c, c->stats.buffer, c->stats.offset);
808 c->stats.buffer = NULL;
809 }
810 } else {
811 out_string(c, "ERROR");
812 }
813 return;
814 }
815
816 /* append terminator and start the transfer */
817 append_stats(NULL, 0, NULL, 0, c);
818
819 if (c->stats.buffer == NULL) {
820 out_of_memory(c, "SERVER_ERROR out of memory writing stats");
821 } else {
822 write_and_free(c, c->stats.buffer, c->stats.offset);
823 c->stats.buffer = NULL;
824 }
825 }
826
827 // slow snprintf for debugging purposes.
828 static void process_meta_command(conn *c, token_t *tokens, const size_t ntokens) {
829 assert(c != NULL);
830
831 if (ntokens < 3 || tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
832 out_string(c, "CLIENT_ERROR bad command line format");
833 return;
834 }
835
836 char *key = tokens[KEY_TOKEN].value;
837 size_t nkey = tokens[KEY_TOKEN].length;
838
839 if (ntokens >= 4 && tokens[2].length == 1 && tokens[2].value[0] == 'b') {
840 size_t ret = base64_decode((unsigned char *)key, nkey,
841 (unsigned char *)key, nkey);
842 if (ret == 0) {
843 // failed to decode.
844 out_string(c, "CLIENT_ERROR bad command line format");
845 return;
846 }
847 nkey = ret;
848 }
849
850 bool overflow; // not used here.
851 item *it = limited_get(key, nkey, c, 0, false, DONT_UPDATE, &overflow);
852 if (it) {
853 mc_resp *resp = c->resp;
854 size_t total = 0;
855 size_t ret;
856 // similar to out_string().
857 memcpy(resp->wbuf, "ME ", 3);
858 total += 3;
859 if (it->it_flags & ITEM_KEY_BINARY) {
860 // re-encode from memory rather than copy the original key;
861 // to help give confidence that what in memory is what we asked
862 // for.
863 total += base64_encode((unsigned char *) ITEM_key(it), it->nkey, (unsigned char *)resp->wbuf + total, WRITE_BUFFER_SIZE - total);
864 } else {
865 memcpy(resp->wbuf + total, ITEM_key(it), it->nkey);
866 total += it->nkey;
867 }
868 resp->wbuf[total] = ' ';
869 total++;
870
871 ret = snprintf(resp->wbuf + total, WRITE_BUFFER_SIZE - (it->nkey + 12),
872 "exp=%d la=%llu cas=%llu fetch=%s cls=%u size=%lu\r\n",
873 (it->exptime == 0) ? -1 : (current_time - it->exptime),
874 (unsigned long long)(current_time - it->time),
875 (unsigned long long)ITEM_get_cas(it),
876 (it->it_flags & ITEM_FETCHED) ? "yes" : "no",
877 ITEM_clsid(it),
878 (unsigned long) ITEM_ntotal(it));
879
880 item_remove(it);
881 resp->wbytes = total + ret;
882 resp_add_iov(resp, resp->wbuf, resp->wbytes);
883 conn_set_state(c, conn_new_cmd);
884 } else {
885 out_string(c, "EN");
886 }
887 pthread_mutex_lock(&c->thread->stats.mutex);
888 c->thread->stats.meta_cmds++;
889 pthread_mutex_unlock(&c->thread->stats.mutex);
890 }
891
892 #define MFLAG_MAX_OPT_LENGTH 20
893 #define MFLAG_MAX_OPAQUE_LENGTH 32
894
895 struct _meta_flags {
896 unsigned int has_error :1; // flipped if we found an error during parsing.
897 unsigned int no_update :1;
898 unsigned int locked :1;
899 unsigned int vivify :1;
900 unsigned int la :1;
901 unsigned int hit :1;
902 unsigned int value :1;
903 unsigned int set_stale :1;
904 unsigned int no_reply :1;
905 unsigned int has_cas :1;
906 unsigned int new_ttl :1;
907 unsigned int key_binary:1;
908 char mode; // single character mode switch, common to ms/ma
909 rel_time_t exptime;
910 rel_time_t autoviv_exptime;
911 rel_time_t recache_time;
912 uint32_t client_flags;
913 uint64_t req_cas_id;
914 uint64_t delta; // ma
915 uint64_t initial; // ma
916 };
917
918 static int _meta_flag_preparse(token_t *tokens, const size_t start,
919 struct _meta_flags *of, char **errstr) {
920 unsigned int i;
921 size_t ret;
922 int32_t tmp_int;
923 uint8_t seen[127] = {0};
924 // Start just past the key token. Look at first character of each token.
925 for (i = start; tokens[i].length != 0; i++) {
926 uint8_t o = (uint8_t)tokens[i].value[0];
927 // zero out repeat flags so we don't over-parse for return data.
928 if (o >= 127 || seen[o] != 0) {
929 *errstr = "CLIENT_ERROR duplicate flag";
930 return -1;
931 }
932 seen[o] = 1;
933 switch (o) {
934 // base64 decode the key in-place, as the binary should always be
935 // shorter and the conversion code buffers bytes.
936 case 'b':
937 ret = base64_decode((unsigned char *)tokens[KEY_TOKEN].value, tokens[KEY_TOKEN].length,
938 (unsigned char *)tokens[KEY_TOKEN].value, tokens[KEY_TOKEN].length);
939 if (ret == 0) {
940 // Failed to decode
941 *errstr = "CLIENT_ERROR error decoding key";
942 of->has_error = 1;
943 }
944 tokens[KEY_TOKEN].length = ret;
945 of->key_binary = 1;
946 break;
947 /* Negative exptimes can underflow and end up immortal. realtime() will
948 immediately expire values that are greater than REALTIME_MAXDELTA, but less
949 than process_started, so lets aim for that. */
950 case 'N':
951 of->locked = 1;
952 of->vivify = 1;
953 if (!safe_strtol(tokens[i].value+1, &tmp_int)) {
954 *errstr = "CLIENT_ERROR bad token in command line format";
955 of->has_error = 1;
956 } else {
957 of->autoviv_exptime = realtime(EXPTIME_TO_POSITIVE_TIME(tmp_int));
958 }
959 break;
960 case 'T':
961 of->locked = 1;
962 if (!safe_strtol(tokens[i].value+1, &tmp_int)) {
963 *errstr = "CLIENT_ERROR bad token in command line format";
964 of->has_error = 1;
965 } else {
966 of->exptime = realtime(EXPTIME_TO_POSITIVE_TIME(tmp_int));
967 of->new_ttl = true;
968 }
969 break;
970 case 'R':
971 of->locked = 1;
972 if (!safe_strtol(tokens[i].value+1, &tmp_int)) {
973 *errstr = "CLIENT_ERROR bad token in command line format";
974 of->has_error = 1;
975 } else {
976 of->recache_time = realtime(EXPTIME_TO_POSITIVE_TIME(tmp_int));
977 }
978 break;
979 case 'l':
980 of->la = 1;
981 of->locked = 1; // need locked to delay LRU bump
982 break;
983 case 'O':
984 case 'P':
985 case 'L':
986 break;
987 case 'k': // known but no special handling
988 case 's':
989 case 't':
990 case 'c':
991 case 'f':
992 break;
993 case 'v':
994 of->value = 1;
995 break;
996 case 'h':
997 of->locked = 1; // need locked to delay LRU bump
998 break;
999 case 'u':
1000 of->no_update = 1;
1001 break;
1002 case 'q':
1003 of->no_reply = 1;
1004 break;
1005 // mset-related.
1006 case 'F':
1007 if (!safe_strtoul(tokens[i].value+1, &of->client_flags)) {
1008 of->has_error = true;
1009 }
1010 break;
1011 case 'C': // mset, mdelete, marithmetic
1012 if (!safe_strtoull(tokens[i].value+1, &of->req_cas_id)) {
1013 *errstr = "CLIENT_ERROR bad token in command line format";
1014 of->has_error = true;
1015 } else {
1016 of->has_cas = true;
1017 }
1018 break;
1019 case 'M': // mset and marithmetic mode switch
1020 if (tokens[i].length != 2) {
1021 *errstr = "CLIENT_ERROR incorrect length for M token";
1022 of->has_error = 1;
1023 } else {
1024 of->mode = tokens[i].value[1];
1025 }
1026 break;
1027 case 'J': // marithmetic initial value
1028 if (!safe_strtoull(tokens[i].value+1, &of->initial)) {
1029 *errstr = "CLIENT_ERROR invalid numeric initial value";
1030 of->has_error = 1;
1031 }
1032 break;
1033 case 'D': // marithmetic delta value
1034 if (!safe_strtoull(tokens[i].value+1, &of->delta)) {
1035 *errstr = "CLIENT_ERROR invalid numeric delta value";
1036 of->has_error = 1;
1037 }
1038 break;
1039 case 'I':
1040 of->set_stale = 1;
1041 break;
1042 default: // unknown flag, bail.
1043 *errstr = "CLIENT_ERROR invalid flag";
1044 return -1;
1045 }
1046 }
1047
1048 return of->has_error ? -1 : 0;
1049 }
1050
1051 static void process_mget_command(conn *c, token_t *tokens, const size_t ntokens) {
1052 char *key;
1053 size_t nkey;
1054 item *it;
1055 unsigned int i = 0;
1056 struct _meta_flags of = {0}; // option bitflags.
1057 uint32_t hv; // cached hash value for unlocking an item.
1058 bool failed = false;
1059 bool item_created = false;
1060 bool won_token = false;
1061 bool ttl_set = false;
1062 char *errstr = "CLIENT_ERROR bad command line format";
1063 assert(c != NULL);
1064 mc_resp *resp = c->resp;
1065 char *p = resp->wbuf;
1066
1067 WANT_TOKENS_MIN(ntokens, 3);
1068
1069 // FIXME: do we move this check to after preparse?
1070 if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
1071 out_errstring(c, "CLIENT_ERROR bad command line format");
1072 return;
1073 }
1074
1075 // NOTE: final token has length == 0.
1076 // KEY_TOKEN == 1. 0 is command.
1077
1078 if (ntokens == 3) {
1079 // TODO: any way to fix this?
1080 out_errstring(c, "CLIENT_ERROR bad command line format");
1081 return;
1082 } else if (ntokens > MFLAG_MAX_OPT_LENGTH) {
1083 // TODO: ensure the command tokenizer gives us at least this many
1084 out_errstring(c, "CLIENT_ERROR options flags are too long");
1085 return;
1086 }
1087
1088 // scrubs duplicated options and sets flags for how to load the item.
1089 // we pass in the first token that should be a flag.
1090 if (_meta_flag_preparse(tokens, 2, &of, &errstr) != 0) {
1091 out_errstring(c, errstr);
1092 return;
1093 }
1094 c->noreply = of.no_reply;
1095
1096 // Grab key and length after meta preparsing in case it was decoded.
1097 key = tokens[KEY_TOKEN].value;
1098 nkey = tokens[KEY_TOKEN].length;
1099
1100 // TODO: need to indicate if the item was overflowed or not?
1101 // I think we do, since an overflow shouldn't trigger an alloc/replace.
1102 bool overflow = false;
1103 if (!of.locked) {
1104 it = limited_get(key, nkey, c, 0, false, !of.no_update, &overflow);
1105 } else {
1106 // If we had to lock the item, we're doing our own bump later.
1107 it = limited_get_locked(key, nkey, c, DONT_UPDATE, &hv, &overflow);
1108 }
1109
1110 // Since we're a new protocol, we can actually inform users that refcount
1111 // overflow is happening by straight up throwing an error.
1112 // We definitely don't want to re-autovivify by accident.
1113 if (overflow) {
1114 assert(it == NULL);
1115 out_errstring(c, "SERVER_ERROR refcount overflow during fetch");
1116 return;
1117 }
1118
1119 if (it == NULL && of.vivify) {
1120 // Fill in the exptime during parsing later.
1121 it = item_alloc(key, nkey, 0, realtime(0), 2);
1122 // We don't actually need any of do_store_item's logic:
1123 // - already fetched and missed an existing item.
1124 // - lock is still held.
1125 // - not append/prepend/replace
1126 // - not testing CAS
1127 if (it != NULL) {
1128 // I look forward to the day I get rid of this :)
1129 memcpy(ITEM_data(it), "\r\n", 2);
1130 // NOTE: This initializes the CAS value.
1131 do_item_link(it, hv);
1132 item_created = true;
1133 }
1134 }
1135
1136 // don't have to check result of add_iov() since the iov size defaults are
1137 // enough.
1138 if (it) {
1139 if (of.value) {
1140 memcpy(p, "VA ", 3);
1141 p = itoa_u32(it->nbytes-2, p+3);
1142 } else {
1143 if (settings.meta_response_old) {
1144 memcpy(p, "OK", 2);
1145 } else {
1146 memcpy(p, "HD", 2);
1147 }
1148 p += 2;
1149 }
1150
1151 for (i = KEY_TOKEN+1; i < ntokens-1; i++) {
1152 switch (tokens[i].value[0]) {
1153 case 'T':
1154 ttl_set = true;
1155 it->exptime = of.exptime;
1156 break;
1157 case 'N':
1158 if (item_created) {
1159 it->exptime = of.autoviv_exptime;
1160 won_token = true;
1161 }
1162 break;
1163 case 'R':
1164 // If we haven't autovivified and supplied token is less
1165 // than current TTL, mark a win.
1166 if ((it->it_flags & ITEM_TOKEN_SENT) == 0
1167 && !item_created
1168 && it->exptime != 0
1169 && it->exptime < of.recache_time) {
1170 won_token = true;
1171 }
1172 break;
1173 case 's':
1174 META_CHAR(p, 's');
1175 p = itoa_u32(it->nbytes-2, p);
1176 break;
1177 case 't':
1178 // TTL remaining as of this request.
1179 // needs to be relative because server clocks may not be in sync.
1180 META_CHAR(p, 't');
1181 if (it->exptime == 0) {
1182 *p = '-';
1183 *(p+1) = '1';
1184 p += 2;
1185 } else {
1186 p = itoa_u32(it->exptime - current_time, p);
1187 }
1188 break;
1189 case 'c':
1190 META_CHAR(p, 'c');
1191 p = itoa_u64(ITEM_get_cas(it), p);
1192 break;
1193 case 'f':
1194 META_CHAR(p, 'f');
1195 if (FLAGS_SIZE(it) == 0) {
1196 *p = '0';
1197 p++;
1198 } else {
1199 p = itoa_u32(*((uint32_t *) ITEM_suffix(it)), p);
1200 }
1201 break;
1202 case 'l':
1203 META_CHAR(p, 'l');
1204 p = itoa_u32(current_time - it->time, p);
1205 break;
1206 case 'h':
1207 META_CHAR(p, 'h');
1208 if (it->it_flags & ITEM_FETCHED) {
1209 *p = '1';
1210 } else {
1211 *p = '0';
1212 }
1213 p++;
1214 break;
1215 case 'O':
1216 if (tokens[i].length > MFLAG_MAX_OPAQUE_LENGTH) {
1217 errstr = "CLIENT_ERROR opaque token too long";
1218 goto error;
1219 }
1220 META_SPACE(p);
1221 memcpy(p, tokens[i].value, tokens[i].length);
1222 p += tokens[i].length;
1223 break;
1224 case 'k':
1225 META_KEY(p, ITEM_key(it), it->nkey, (it->it_flags & ITEM_KEY_BINARY));
1226 break;
1227 }
1228 }
1229
1230 // Has this item already sent a token?
1231 // Important to do this here so we don't send W with Z.
1232 // Isn't critical, but easier for client authors to understand.
1233 if (it->it_flags & ITEM_TOKEN_SENT) {
1234 META_CHAR(p, 'Z');
1235 }
1236 if (it->it_flags & ITEM_STALE) {
1237 META_CHAR(p, 'X');
1238 // FIXME: think hard about this. is this a default, or a flag?
1239 if ((it->it_flags & ITEM_TOKEN_SENT) == 0) {
1240 // If we're stale but no token already sent, now send one.
1241 won_token = true;
1242 }
1243 }
1244
1245 if (won_token) {
1246 // Mark a win into the flag buffer.
1247 META_CHAR(p, 'W');
1248 it->it_flags |= ITEM_TOKEN_SENT;
1249 }
1250
1251 *p = '\r';
1252 *(p+1) = '\n';
1253 *(p+2) = '\0';
1254 p += 2;
1255 // finally, chain in the buffer.
1256 resp_add_iov(resp, resp->wbuf, p - resp->wbuf);
1257
1258 if (of.value) {
1259 #ifdef EXTSTORE
1260 if (it->it_flags & ITEM_HDR) {
1261 if (storage_get_item(c, it, resp) != 0) {
1262 pthread_mutex_lock(&c->thread->stats.mutex);
1263 c->thread->stats.get_oom_extstore++;
1264 pthread_mutex_unlock(&c->thread->stats.mutex);
1265
1266 failed = true;
1267 }
1268 } else if ((it->it_flags & ITEM_CHUNKED) == 0) {
1269 resp_add_iov(resp, ITEM_data(it), it->nbytes);
1270 } else {
1271 resp_add_chunked_iov(resp, it, it->nbytes);
1272 }
1273 #else
1274 if ((it->it_flags & ITEM_CHUNKED) == 0) {
1275 resp_add_iov(resp, ITEM_data(it), it->nbytes);
1276 } else {
1277 resp_add_chunked_iov(resp, it, it->nbytes);
1278 }
1279 #endif
1280 }
1281
1282 // need to hold the ref at least because of the key above.
1283 #ifdef EXTSTORE
1284 if (!failed) {
1285 if ((it->it_flags & ITEM_HDR) != 0 && of.value) {
1286 // Only have extstore clean if header and returning value.
1287 resp->item = NULL;
1288 } else {
1289 resp->item = it;
1290 }
1291 } else {
1292 // Failed to set up extstore fetch.
1293 if (of.locked) {
1294 do_item_remove(it);
1295 } else {
1296 item_remove(it);
1297 }
1298 }
1299 #else
1300 resp->item = it;
1301 #endif
1302 } else {
1303 failed = true;
1304 }
1305
1306 if (of.locked) {
1307 // Delayed bump so we could get fetched/last access time pre-update.
1308 if (!of.no_update && it != NULL) {
1309 do_item_bump(c, it, hv);
1310 }
1311 item_unlock(hv);
1312 }
1313
1314 // we count this command as a normal one if we've gotten this far.
1315 // TODO: for autovivify case, miss never happens. Is this okay?
1316 if (!failed) {
1317 pthread_mutex_lock(&c->thread->stats.mutex);
1318 if (ttl_set) {
1319 c->thread->stats.touch_cmds++;
1320 c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
1321 } else {
1322 c->thread->stats.lru_hits[it->slabs_clsid]++;
1323 c->thread->stats.get_cmds++;
1324 }
1325 pthread_mutex_unlock(&c->thread->stats.mutex);
1326
1327 conn_set_state(c, conn_new_cmd);
1328 } else {
1329 pthread_mutex_lock(&c->thread->stats.mutex);
1330 if (ttl_set) {
1331 c->thread->stats.touch_cmds++;
1332 c->thread->stats.touch_misses++;
1333 } else {
1334 c->thread->stats.get_misses++;
1335 c->thread->stats.get_cmds++;
1336 }
1337 MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
1338 pthread_mutex_unlock(&c->thread->stats.mutex);
1339
1340 // This gets elided in noreply mode.
1341 out_string(c, "EN");
1342 }
1343 return;
1344 error:
1345 if (it) {
1346 do_item_remove(it);
1347 if (of.locked) {
1348 item_unlock(hv);
1349 }
1350 }
1351 out_errstring(c, errstr);
1352 }
1353
1354 static void process_mset_command(conn *c, token_t *tokens, const size_t ntokens) {
1355 char *key;
1356 size_t nkey;
1357 item *it;
1358 int i;
1359 short comm = NREAD_SET;
1360 struct _meta_flags of = {0}; // option bitflags.
1361 char *errstr = "CLIENT_ERROR bad command line format";
1362 uint32_t hv; // cached hash value.
1363 int vlen = 0; // value from data line.
1364 assert(c != NULL);
1365 mc_resp *resp = c->resp;
1366 char *p = resp->wbuf;
1367
1368 WANT_TOKENS_MIN(ntokens, 3);
1369
1370 // TODO: most of this is identical to mget.
1371 if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
1372 out_errstring(c, "CLIENT_ERROR bad command line format");
1373 return;
1374 }
1375
1376 if (ntokens == 3) {
1377 out_errstring(c, "CLIENT_ERROR bad command line format");
1378 return;
1379 }
1380
1381 if (ntokens > MFLAG_MAX_OPT_LENGTH) {
1382 out_errstring(c, "CLIENT_ERROR options flags too long");
1383 return;
1384 }
1385
1386 // We note tokens into the front of the write buffer, so we can create the
1387 // final buffer in complete_nread_ascii.
1388 p = resp->wbuf;
1389
1390 if (!safe_strtol(tokens[KEY_TOKEN + 1].value, (int32_t*)&vlen)) {
1391 out_errstring(c, "CLIENT_ERROR bad command line format");
1392 return;
1393 }
1394
1395 if (vlen < 0 || vlen > (INT_MAX - 2)) {
1396 out_errstring(c, "CLIENT_ERROR bad command line format");
1397 return;
1398 }
1399 vlen += 2;
1400
1401 // We need to at least try to get the size to properly slurp bad bytes
1402 // after an error.
1403 // we pass in the first token that should be a flag.
1404 if (_meta_flag_preparse(tokens, 3, &of, &errstr) != 0) {
1405 goto error;
1406 }
1407
1408 key = tokens[KEY_TOKEN].value;
1409 nkey = tokens[KEY_TOKEN].length;
1410
1411 // Set noreply after tokens are understood.
1412 c->noreply = of.no_reply;
1413 // Clear cas return value
1414 c->cas = 0;
1415
1416 bool has_error = false;
1417 for (i = KEY_TOKEN+1; i < ntokens-1; i++) {
1418 switch (tokens[i].value[0]) {
1419 // TODO: macro perhaps?
1420 case 'O':
1421 if (tokens[i].length > MFLAG_MAX_OPAQUE_LENGTH) {
1422 errstr = "CLIENT_ERROR opaque token too long";
1423 has_error = true;
1424 break;
1425 }
1426 META_SPACE(p);
1427 memcpy(p, tokens[i].value, tokens[i].length);
1428 p += tokens[i].length;
1429 break;
1430 case 'k':
1431 META_CHAR(p, 'k');
1432 break;
1433 case 'c':
1434 // need to set the cas value post-assignment.
1435 META_CHAR(p, 'c');
1436 break;
1437 }
1438 }
1439
1440 // "mode switch" to alternative commands
1441 switch (of.mode) {
1442 case 0:
1443 break; // no mode supplied.
1444 case 'E': // Add...
1445 comm = NREAD_ADD;
1446 break;
1447 case 'A': // Append.
1448 comm = NREAD_APPEND;
1449 break;
1450 case 'P': // Prepend.
1451 comm = NREAD_PREPEND;
1452 break;
1453 case 'R': // Replace.
1454 comm = NREAD_REPLACE;
1455 break;
1456 case 'S': // Set. Default.
1457 comm = NREAD_SET;
1458 break;
1459 default:
1460 errstr = "CLIENT_ERROR invalid mode for ms M token";
1461 goto error;
1462 }
1463
1464 // The item storage function doesn't exactly map to mset.
1465 // If a CAS value is supplied, upgrade default SET mode to CAS mode.
1466 // Also allows REPLACE to work, as REPLACE + CAS works the same as CAS.
1467 // add-with-cas works the same as add; but could only LRU bump if match..
1468 // APPEND/PREPEND allow a simplified CAS check.
1469 if (of.has_cas && (comm == NREAD_SET || comm == NREAD_REPLACE)) {
1470 comm = NREAD_CAS;
1471 }
1472
1473 // We attempt to process as much as we can in hopes of getting a valid and
1474 // adjusted vlen, or else the data swallowed after error will be for 0b.
1475 if (has_error)
1476 goto error;
1477
1478 it = item_alloc(key, nkey, of.client_flags, of.exptime, vlen);
1479
1480 if (it == 0) {
1481 enum store_item_type status;
1482 // TODO: These could be normalized codes (TL and OM). Need to
1483 // reorganize the output stuff a bit though.
1484 if (! item_size_ok(nkey, of.client_flags, vlen)) {
1485 errstr = "SERVER_ERROR object too large for cache";
1486 status = TOO_LARGE;
1487 pthread_mutex_lock(&c->thread->stats.mutex);
1488 c->thread->stats.store_too_large++;
1489 pthread_mutex_unlock(&c->thread->stats.mutex);
1490 } else {
1491 errstr = "SERVER_ERROR out of memory storing object";
1492 status = NO_MEMORY;
1493 pthread_mutex_lock(&c->thread->stats.mutex);
1494 c->thread->stats.store_no_memory++;
1495 pthread_mutex_unlock(&c->thread->stats.mutex);
1496 }
1497 // FIXME: LOGGER_LOG specific to mset, include options.
1498 LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE,
1499 NULL, status, comm, key, nkey, 0, 0);
1500
1501 /* Avoid stale data persisting in cache because we failed alloc. */
1502 // NOTE: only if SET mode?
1503 it = item_get_locked(key, nkey, c, DONT_UPDATE, &hv);
1504 if (it) {
1505 do_item_unlink(it, hv);
1506 STORAGE_delete(c->thread->storage, it);
1507 do_item_remove(it);
1508 }
1509 item_unlock(hv);
1510
1511 goto error;
1512 }
1513 ITEM_set_cas(it, of.req_cas_id);
1514
1515 c->item = it;
1516 #ifdef NEED_ALIGN
1517 if (it->it_flags & ITEM_CHUNKED) {
1518 c->ritem = ITEM_schunk(it);
1519 } else {
1520 c->ritem = ITEM_data(it);
1521 }
1522 #else
1523 c->ritem = ITEM_data(it);
1524 #endif
1525 c->rlbytes = it->nbytes;
1526 c->cmd = comm;
1527
1528 // Prevent printing back the key in meta commands as garbage.
1529 if (of.key_binary) {
1530 it->it_flags |= ITEM_KEY_BINARY;
1531 }
1532
1533 if (of.set_stale && comm == NREAD_CAS) {
1534 c->set_stale = true;
1535 }
1536 resp->wbytes = p - resp->wbuf;
1537 // we don't set up the iov here, instead after complete_nread_ascii when
1538 // we have the full status code and item data.
1539 c->mset_res = true;
1540 conn_set_state(c, conn_nread);
1541 return;
1542 error:
1543 /* swallow the data line */
1544 c->sbytes = vlen;
1545
1546 // Note: no errors possible after the item was successfully allocated.
1547 // So we're just looking at dumping error codes and returning.
1548 out_errstring(c, errstr);
1549 // TODO: pass state in? else switching twice meh.
1550 conn_set_state(c, conn_swallow);
1551 }
1552
1553 static void process_mdelete_command(conn *c, token_t *tokens, const size_t ntokens) {
1554 char *key;
1555 size_t nkey;
1556 item *it = NULL;
1557 int i;
1558 uint32_t hv;
1559 struct _meta_flags of = {0}; // option bitflags.
1560 char *errstr = "CLIENT_ERROR bad command line format";
1561 assert(c != NULL);
1562 mc_resp *resp = c->resp;
1563 // reserve 3 bytes for status code
1564 char *p = resp->wbuf + 3;
1565
1566 WANT_TOKENS_MIN(ntokens, 3);
1567
1568 // TODO: most of this is identical to mget.
1569 if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
1570 out_string(c, "CLIENT_ERROR bad command line format");
1571 return;
1572 }
1573
1574 if (ntokens > MFLAG_MAX_OPT_LENGTH) {
1575 out_string(c, "CLIENT_ERROR options flags too long");
1576 return;
1577 }
1578
1579 // scrubs duplicated options and sets flags for how to load the item.
1580 // we pass in the first token that should be a flag.
1581 // FIXME: not using the preparse errstr?
1582 if (_meta_flag_preparse(tokens, 2, &of, &errstr) != 0) {
1583 out_errstring(c, "CLIENT_ERROR invalid or duplicate flag");
1584 return;
1585 }
1586 assert(c != NULL);
1587 c->noreply = of.no_reply;
1588
1589 key = tokens[KEY_TOKEN].value;
1590 nkey = tokens[KEY_TOKEN].length;
1591
1592 for (i = KEY_TOKEN+1; i < ntokens-1; i++) {
1593 switch (tokens[i].value[0]) {
1594 // TODO: macro perhaps?
1595 case 'O':
1596 if (tokens[i].length > MFLAG_MAX_OPAQUE_LENGTH) {
1597 errstr = "CLIENT_ERROR opaque token too long";
1598 goto error;
1599 }
1600 META_SPACE(p);
1601 memcpy(p, tokens[i].value, tokens[i].length);
1602 p += tokens[i].length;
1603 break;
1604 case 'k':
1605 META_KEY(p, key, nkey, of.key_binary);
1606 break;
1607 }
1608 }
1609
1610 it = item_get_locked(key, nkey, c, DONT_UPDATE, &hv);
1611 if (it) {
1612 MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
1613
1614 // allow only deleting/marking if a CAS value matches.
1615 if (of.has_cas && ITEM_get_cas(it) != of.req_cas_id) {
1616 pthread_mutex_lock(&c->thread->stats.mutex);
1617 c->thread->stats.delete_misses++;
1618 pthread_mutex_unlock(&c->thread->stats.mutex);
1619
1620 memcpy(resp->wbuf, "EX ", 3);
1621 goto cleanup;
1622 }
1623
1624 // If we're to set this item as stale, we don't actually want to
1625 // delete it. We mark the stale bit, bump CAS, and update exptime if
1626 // we were supplied a new TTL.
1627 if (of.set_stale) {
1628 if (of.new_ttl) {
1629 it->exptime = of.exptime;
1630 }
1631 it->it_flags |= ITEM_STALE;
1632 // Also need to remove TOKEN_SENT, so next client can win.
1633 it->it_flags &= ~ITEM_TOKEN_SENT;
1634
1635 ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
1636
1637 // Clients can noreply nominal responses.
1638 if (c->noreply)
1639 resp->skip = true;
1640 if (settings.meta_response_old) {
1641 memcpy(resp->wbuf, "OK ", 3);
1642 } else {
1643 memcpy(resp->wbuf, "HD ", 3);
1644 }
1645 } else {
1646 pthread_mutex_lock(&c->thread->stats.mutex);
1647 c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
1648 pthread_mutex_unlock(&c->thread->stats.mutex);
1649
1650 do_item_unlink(it, hv);
1651 STORAGE_delete(c->thread->storage, it);
1652 if (c->noreply)
1653 resp->skip = true;
1654 if (settings.meta_response_old) {
1655 memcpy(resp->wbuf, "OK ", 3);
1656 } else {
1657 memcpy(resp->wbuf, "HD ", 3);
1658 }
1659 }
1660 goto cleanup;
1661 } else {
1662 pthread_mutex_lock(&c->thread->stats.mutex);
1663 c->thread->stats.delete_misses++;
1664 pthread_mutex_unlock(&c->thread->stats.mutex);
1665
1666 memcpy(resp->wbuf, "NF ", 3);
1667 goto cleanup;
1668 }
1669 cleanup:
1670 if (it) {
1671 do_item_remove(it);
1672 }
1673 // Item is always returned locked, even if missing.
1674 item_unlock(hv);
1675 resp->wbytes = p - resp->wbuf;
1676 memcpy(resp->wbuf + resp->wbytes, "\r\n", 2);
1677 resp->wbytes += 2;
1678 resp_add_iov(resp, resp->wbuf, resp->wbytes);
1679 conn_set_state(c, conn_new_cmd);
1680 return;
1681 error:
1682 out_errstring(c, errstr);
1683 }
1684
1685 static void process_marithmetic_command(conn *c, token_t *tokens, const size_t ntokens) {
1686 char *key;
1687 size_t nkey;
1688 int i;
1689 struct _meta_flags of = {0}; // option bitflags.
1690 char *errstr = "CLIENT_ERROR bad command line format";
1691 assert(c != NULL);
1692 mc_resp *resp = c->resp;
1693 // no reservation (like del/set) since we post-process the status line.
1694 char *p = resp->wbuf;
1695
1696 // If no argument supplied, incr or decr by one.
1697 of.delta = 1;
1698 of.initial = 0; // redundant, for clarity.
1699 bool incr = true; // default mode is to increment.
1700 bool locked = false;
1701 uint32_t hv = 0;
1702 item *it = NULL; // item returned by do_add_delta.
1703
1704 WANT_TOKENS_MIN(ntokens, 3);
1705
1706 // TODO: most of this is identical to mget.
1707 if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
1708 out_string(c, "CLIENT_ERROR bad command line format");
1709 return;
1710 }
1711
1712 if (ntokens > MFLAG_MAX_OPT_LENGTH) {
1713 out_string(c, "CLIENT_ERROR options flags too long");
1714 return;
1715 }
1716
1717 // scrubs duplicated options and sets flags for how to load the item.
1718 // we pass in the first token that should be a flag.
1719 if (_meta_flag_preparse(tokens, 2, &of, &errstr) != 0) {
1720 out_errstring(c, "CLIENT_ERROR invalid or duplicate flag");
1721 return;
1722 }
1723 assert(c != NULL);
1724 c->noreply = of.no_reply;
1725
1726 key = tokens[KEY_TOKEN].value;
1727 nkey = tokens[KEY_TOKEN].length;
1728
1729 // "mode switch" to alternative commands
1730 switch (of.mode) {
1731 case 0: // no switch supplied.
1732 break;
1733 case 'I': // Incr (default)
1734 case '+':
1735 incr = true;
1736 break;
1737 case 'D': // Decr.
1738 case '-':
1739 incr = false;
1740 break;
1741 default:
1742 errstr = "CLIENT_ERROR invalid mode for ma M token";
1743 goto error;
1744 break;
1745 }
1746
1747 // take hash value and manually lock item... hold lock during store phase
1748 // on miss and avoid recalculating the hash multiple times.
1749 hv = hash(key, nkey);
1750 item_lock(hv);
1751 locked = true;
1752 char tmpbuf[INCR_MAX_STORAGE_LEN];
1753
1754 // return a referenced item if it exists, so we can modify it here, rather
1755 // than adding even more parameters to do_add_delta.
1756 bool item_created = false;
1757 switch(do_add_delta(c, key, nkey, incr, of.delta, tmpbuf, &of.req_cas_id, hv, &it)) {
1758 case OK:
1759 if (c->noreply)
1760 resp->skip = true;
1761 if (settings.meta_response_old) {
1762 memcpy(resp->wbuf, "OK ", 3);
1763 } else {
1764 memcpy(resp->wbuf, "HD ", 3);
1765 }
1766 break;
1767 case NON_NUMERIC:
1768 errstr = "CLIENT_ERROR cannot increment or decrement non-numeric value";
1769 goto error;
1770 break;
1771 case EOM:
1772 errstr = "SERVER_ERROR out of memory";
1773 goto error;
1774 break;
1775 case DELTA_ITEM_NOT_FOUND:
1776 if (of.vivify) {
1777 itoa_u64(of.initial, tmpbuf);
1778 int vlen = strlen(tmpbuf);
1779
1780 it = item_alloc(key, nkey, 0, 0, vlen+2);
1781 if (it != NULL) {
1782 memcpy(ITEM_data(it), tmpbuf, vlen);
1783 memcpy(ITEM_data(it) + vlen, "\r\n", 2);
1784 if (do_store_item(it, NREAD_ADD, c, hv)) {
1785 item_created = true;
1786 } else {
1787 // Not sure how we can get here if we're holding the lock.
1788 memcpy(resp->wbuf, "NS ", 3);
1789 }
1790 } else {
1791 errstr = "SERVER_ERROR Out of memory allocating new item";
1792 goto error;
1793 }
1794 } else {
1795 pthread_mutex_lock(&c->thread->stats.mutex);
1796 if (incr) {
1797 c->thread->stats.incr_misses++;
1798 } else {
1799 c->thread->stats.decr_misses++;
1800 }
1801 pthread_mutex_unlock(&c->thread->stats.mutex);
1802 // won't have a valid it here.
1803 memcpy(p, "NF ", 3);
1804 p += 3;
1805 }
1806 break;
1807 case DELTA_ITEM_CAS_MISMATCH:
1808 // also returns without a valid it.
1809 memcpy(p, "EX ", 3);
1810 p += 3;
1811 break;
1812 }
1813
1814 // final loop
1815 // allows building the response with information after vivifying from a
1816 // miss, or returning a new CAS value after add_delta().
1817 if (it) {
1818 size_t vlen = strlen(tmpbuf);
1819 if (of.value) {
1820 memcpy(p, "VA ", 3);
1821 p = itoa_u32(vlen, p+3);
1822 } else {
1823 if (settings.meta_response_old) {
1824 memcpy(p, "OK", 2);
1825 } else {
1826 memcpy(p, "HD", 2);
1827 }
1828 p += 2;
1829 }
1830
1831 for (i = KEY_TOKEN+1; i < ntokens-1; i++) {
1832 switch (tokens[i].value[0]) {
1833 case 'c':
1834 META_CHAR(p, 'c');
1835 p = itoa_u64(ITEM_get_cas(it), p);
1836 break;
1837 case 't':
1838 META_CHAR(p, 't');
1839 if (it->exptime == 0) {
1840 *p = '-';
1841 *(p+1) = '1';
1842 p += 2;
1843 } else {
1844 p = itoa_u32(it->exptime - current_time, p);
1845 }
1846 break;
1847 case 'T':
1848 it->exptime = of.exptime;
1849 break;
1850 case 'N':
1851 if (item_created) {
1852 it->exptime = of.autoviv_exptime;
1853 }
1854 break;
1855 // TODO: macro perhaps?
1856 case 'O':
1857 if (tokens[i].length > MFLAG_MAX_OPAQUE_LENGTH) {
1858 errstr = "CLIENT_ERROR opaque token too long";
1859 goto error;
1860 }
1861 META_SPACE(p);
1862 memcpy(p, tokens[i].value, tokens[i].length);
1863 p += tokens[i].length;
1864 break;
1865 case 'k':
1866 META_KEY(p, key, nkey, of.key_binary);
1867 break;
1868 }
1869 }
1870
1871 if (of.value) {
1872 *p = '\r';
1873 *(p+1) = '\n';
1874 p += 2;
1875 memcpy(p, tmpbuf, vlen);
1876 p += vlen;
1877 }
1878
1879 do_item_remove(it);
1880 } else {
1881 // No item to handle. still need to return opaque/key tokens
1882 for (i = KEY_TOKEN+1; i < ntokens-1; i++) {
1883 switch (tokens[i].value[0]) {
1884 // TODO: macro perhaps?
1885 case 'O':
1886 if (tokens[i].length > MFLAG_MAX_OPAQUE_LENGTH) {
1887 errstr = "CLIENT_ERROR opaque token too long";
1888 goto error;
1889 }
1890 META_SPACE(p);
1891 memcpy(p, tokens[i].value, tokens[i].length);
1892 p += tokens[i].length;
1893 break;
1894 case 'k':
1895 META_KEY(p, key, nkey, of.key_binary);
1896 break;
1897 }
1898 }
1899 }
1900
1901 item_unlock(hv);
1902
1903 resp->wbytes = p - resp->wbuf;
1904 memcpy(resp->wbuf + resp->wbytes, "\r\n", 2);
1905 resp->wbytes += 2;
1906 resp_add_iov(resp, resp->wbuf, resp->wbytes);
1907 conn_set_state(c, conn_new_cmd);
1908 return;
1909 error:
1910 if (it != NULL)
1911 do_item_remove(it);
1912 if (locked)
1913 item_unlock(hv);
1914 out_errstring(c, errstr);
1915 }
1916
1917
1918 static void process_update_command(conn *c, token_t *tokens, const size_t ntokens, int comm, bool handle_cas) {
1919 char *key;
1920 size_t nkey;
1921 unsigned int flags;
1922 int32_t exptime_int = 0;
1923 rel_time_t exptime = 0;
1924 int vlen;
1925 uint64_t req_cas_id=0;
1926 item *it;
1927
1928 assert(c != NULL);
1929
1930 set_noreply_maybe(c, tokens, ntokens);
1931
1932 if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
1933 out_string(c, "CLIENT_ERROR bad command line format");
1934 return;
1935 }
1936
1937 key = tokens[KEY_TOKEN].value;
1938 nkey = tokens[KEY_TOKEN].length;
1939
1940 if (! (safe_strtoul(tokens[2].value, (uint32_t *)&flags)
1941 && safe_strtol(tokens[3].value, &exptime_int)
1942 && safe_strtol(tokens[4].value, (int32_t *)&vlen))) {
1943 out_string(c, "CLIENT_ERROR bad command line format");
1944 return;
1945 }
1946
1947 exptime = realtime(EXPTIME_TO_POSITIVE_TIME(exptime_int));
1948
1949 // does cas value exist?
1950 if (handle_cas) {
1951 if (!safe_strtoull(tokens[5].value, &req_cas_id)) {
1952 out_string(c, "CLIENT_ERROR bad command line format");
1953 return;
1954 }
1955 }
1956
1957 if (vlen < 0 || vlen > (INT_MAX - 2)) {
1958 out_string(c, "CLIENT_ERROR bad command line format");
1959 return;
1960 }
1961 vlen += 2;
1962
1963 if (settings.detail_enabled) {
1964 stats_prefix_record_set(key, nkey);
1965 }
1966
1967 it = item_alloc(key, nkey, flags, exptime, vlen);
1968
1969 if (it == 0) {
1970 enum store_item_type status;
1971 if (! item_size_ok(nkey, flags, vlen)) {
1972 out_string(c, "SERVER_ERROR object too large for cache");
1973 status = TOO_LARGE;
1974 pthread_mutex_lock(&c->thread->stats.mutex);
1975 c->thread->stats.store_too_large++;
1976 pthread_mutex_unlock(&c->thread->stats.mutex);
1977 } else {
1978 out_of_memory(c, "SERVER_ERROR out of memory storing object");
1979 status = NO_MEMORY;
1980 pthread_mutex_lock(&c->thread->stats.mutex);
1981 c->thread->stats.store_no_memory++;
1982 pthread_mutex_unlock(&c->thread->stats.mutex);
1983 }
1984 LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE,
1985 NULL, status, comm, key, nkey, 0, 0, c->sfd);
1986 /* swallow the data line */
1987 conn_set_state(c, conn_swallow);
1988 c->sbytes = vlen;
1989
1990 /* Avoid stale data persisting in cache because we failed alloc.
1991 * Unacceptable for SET. Anywhere else too? */
1992 if (comm == NREAD_SET) {
1993 it = item_get(key, nkey, c, DONT_UPDATE);
1994 if (it) {
1995 item_unlink(it);
1996 STORAGE_delete(c->thread->storage, it);
1997 item_remove(it);
1998 }
1999 }
2000
2001 return;
2002 }
2003 ITEM_set_cas(it, req_cas_id);
2004
2005 c->item = it;
2006 #ifdef NEED_ALIGN
2007 if (it->it_flags & ITEM_CHUNKED) {
2008 c->ritem = ITEM_schunk(it);
2009 } else {
2010 c->ritem = ITEM_data(it);
2011 }
2012 #else
2013 c->ritem = ITEM_data(it);
2014 #endif
2015 c->rlbytes = it->nbytes;
2016 c->cmd = comm;
2017 conn_set_state(c, conn_nread);
2018 }
2019
2020 static void process_touch_command(conn *c, token_t *tokens, const size_t ntokens) {
2021 char *key;
2022 size_t nkey;
2023 int32_t exptime_int = 0;
2024 rel_time_t exptime = 0;
2025 item *it;
2026
2027 assert(c != NULL);
2028
2029 set_noreply_maybe(c, tokens, ntokens);
2030
2031 if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
2032 out_string(c, "CLIENT_ERROR bad command line format");
2033 return;
2034 }
2035
2036 key = tokens[KEY_TOKEN].value;
2037 nkey = tokens[KEY_TOKEN].length;
2038
2039 if (!safe_strtol(tokens[2].value, &exptime_int)) {
2040 out_string(c, "CLIENT_ERROR invalid exptime argument");
2041 return;
2042 }
2043
2044 exptime = realtime(EXPTIME_TO_POSITIVE_TIME(exptime_int));
2045 it = item_touch(key, nkey, exptime, c);
2046 if (it) {
2047 pthread_mutex_lock(&c->thread->stats.mutex);
2048 c->thread->stats.touch_cmds++;
2049 c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
2050 pthread_mutex_unlock(&c->thread->stats.mutex);
2051
2052 out_string(c, "TOUCHED");
2053 item_remove(it);
2054 } else {
2055 pthread_mutex_lock(&c->thread->stats.mutex);
2056 c->thread->stats.touch_cmds++;
2057 c->thread->stats.touch_misses++;
2058 pthread_mutex_unlock(&c->thread->stats.mutex);
2059
2060 out_string(c, "NOT_FOUND");
2061 }
2062 }
2063
2064 static void process_arithmetic_command(conn *c, token_t *tokens, const size_t ntokens, const bool incr) {
2065 char temp[INCR_MAX_STORAGE_LEN];
2066 uint64_t delta;
2067 char *key;
2068 size_t nkey;
2069
2070 assert(c != NULL);
2071
2072 set_noreply_maybe(c, tokens, ntokens);
2073
2074 if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
2075 out_string(c, "CLIENT_ERROR bad command line format");
2076 return;
2077 }
2078
2079 key = tokens[KEY_TOKEN].value;
2080 nkey = tokens[KEY_TOKEN].length;
2081
2082 if (!safe_strtoull(tokens[2].value, &delta)) {
2083 out_string(c, "CLIENT_ERROR invalid numeric delta argument");
2084 return;
2085 }
2086
2087 switch(add_delta(c, key, nkey, incr, delta, temp, NULL)) {
2088 case OK:
2089 out_string(c, temp);
2090 break;
2091 case NON_NUMERIC:
2092 out_string(c, "CLIENT_ERROR cannot increment or decrement non-numeric value");
2093 break;
2094 case EOM:
2095 out_of_memory(c, "SERVER_ERROR out of memory");
2096 break;
2097 case DELTA_ITEM_NOT_FOUND:
2098 pthread_mutex_lock(&c->thread->stats.mutex);
2099 if (incr) {
2100 c->thread->stats.incr_misses++;
2101 } else {
2102 c->thread->stats.decr_misses++;
2103 }
2104 pthread_mutex_unlock(&c->thread->stats.mutex);
2105
2106 out_string(c, "NOT_FOUND");
2107 break;
2108 case DELTA_ITEM_CAS_MISMATCH:
2109 break; /* Should never get here */
2110 }
2111 }
2112
2113
2114 static void process_delete_command(conn *c, token_t *tokens, const size_t ntokens) {
2115 char *key;
2116 size_t nkey;
2117 item *it;
2118 uint32_t hv;
2119
2120 assert(c != NULL);
2121
2122 if (ntokens > 3) {
2123 bool hold_is_zero = strcmp(tokens[KEY_TOKEN+1].value, "0") == 0;
2124 bool sets_noreply = set_noreply_maybe(c, tokens, ntokens);
2125 bool valid = (ntokens == 4 && (hold_is_zero || sets_noreply))
2126 || (ntokens == 5 && hold_is_zero && sets_noreply);
2127 if (!valid) {
2128 out_string(c, "CLIENT_ERROR bad command line format. "
2129 "Usage: delete <key> [noreply]");
2130 return;
2131 }
2132 }
2133
2134
2135 key = tokens[KEY_TOKEN].value;
2136 nkey = tokens[KEY_TOKEN].length;
2137
2138 if(nkey > KEY_MAX_LENGTH) {
2139 out_string(c, "CLIENT_ERROR bad command line format");
2140 return;
2141 }
2142
2143 if (settings.detail_enabled) {
2144 stats_prefix_record_delete(key, nkey);
2145 }
2146
2147 it = item_get_locked(key, nkey, c, DONT_UPDATE, &hv);
2148 if (it) {
2149 MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
2150
2151 pthread_mutex_lock(&c->thread->stats.mutex);
2152 c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
2153 pthread_mutex_unlock(&c->thread->stats.mutex);
2154
2155 do_item_unlink(it, hv);
2156 STORAGE_delete(c->thread->storage, it);
2157 do_item_remove(it); /* release our reference */
2158 out_string(c, "DELETED");
2159 } else {
2160 pthread_mutex_lock(&c->thread->stats.mutex);
2161 c->thread->stats.delete_misses++;
2162 pthread_mutex_unlock(&c->thread->stats.mutex);
2163
2164 out_string(c, "NOT_FOUND");
2165 }
2166 item_unlock(hv);
2167 }
2168
2169 static void process_verbosity_command(conn *c, token_t *tokens, const size_t ntokens) {
2170 unsigned int level;
2171
2172 assert(c != NULL);
2173
2174 set_noreply_maybe(c, tokens, ntokens);
2175
2176 if (!safe_strtoul(tokens[1].value, (uint32_t*)&level)) {
2177 out_string(c, "CLIENT_ERROR bad command line format");
2178 return;
2179 }
2180 settings.verbose = level > MAX_VERBOSITY_LEVEL ? MAX_VERBOSITY_LEVEL : level;
2181 out_string(c, "OK");
2182 return;
2183 }
2184
2185 #ifdef MEMCACHED_DEBUG
2186 static void process_misbehave_command(conn *c) {
2187 int allowed = 0;
2188
2189 // try opening new TCP socket
2190 int i = socket(AF_INET, SOCK_STREAM, 0);
2191 if (i != -1) {
2192 allowed++;
2193 close(i);
2194 }
2195
2196 // try executing new commands
2197 i = system("sleep 0");
2198 if (i != -1) {
2199 allowed++;
2200 }
2201
2202 if (allowed) {
2203 out_string(c, "ERROR");
2204 } else {
2205 out_string(c, "OK");
2206 }
2207 }
2208
2209 static void process_debugtime_command(conn *c, token_t *tokens, const size_t ntokens) {
2210 if (strcmp(tokens[1].value, "p") == 0) {
2211 if (!is_paused) {
2212 is_paused = true;
2213 }
2214 } else if (strcmp(tokens[1].value, "r") == 0) {
2215 if (is_paused) {
2216 is_paused = false;
2217 }
2218 } else {
2219 int64_t time_delta = 0;
2220 if (!safe_strtoll(tokens[1].value, &time_delta)) {
2221 out_string(c, "ERROR");
2222 return;
2223 }
2224 delta += time_delta;
2225 current_time += delta;
2226 }
2227 out_string(c, "OK");
2228 }
2229 #endif
2230
2231 static void process_slabs_automove_command(conn *c, token_t *tokens, const size_t ntokens) {
2232 unsigned int level;
2233 double ratio;
2234
2235 assert(c != NULL);
2236
2237 set_noreply_maybe(c, tokens, ntokens);
2238
2239 if (strcmp(tokens[2].value, "ratio") == 0) {
2240 if (ntokens < 5 || !safe_strtod(tokens[3].value, &ratio)) {
2241 out_string(c, "ERROR");
2242 return;
2243 }
2244 settings.slab_automove_ratio = ratio;
2245 } else {
2246 if (!safe_strtoul(tokens[2].value, (uint32_t*)&level)) {
2247 out_string(c, "CLIENT_ERROR bad command line format");
2248 return;
2249 }
2250 if (level == 0) {
2251 settings.slab_automove = 0;
2252 } else if (level == 1 || level == 2) {
2253 settings.slab_automove = level;
2254 } else {
2255 out_string(c, "ERROR");
2256 return;
2257 }
2258 }
2259 out_string(c, "OK");
2260 return;
2261 }
2262
2263 /* TODO: decide on syntax for sampling? */
2264 static void process_watch_command(conn *c, token_t *tokens, const size_t ntokens) {
2265 uint16_t f = 0;
2266 int x;
2267 assert(c != NULL);
2268
2269 set_noreply_maybe(c, tokens, ntokens);
2270 if (!settings.watch_enabled) {
2271 out_string(c, "CLIENT_ERROR watch commands not allowed");
2272 return;
2273 }
2274
2275 if (resp_has_stack(c)) {
2276 out_string(c, "ERROR cannot pipeline other commands before watch");
2277 return;
2278 }
2279
2280 if (ntokens > 2) {
2281 for (x = COMMAND_TOKEN + 1; x < ntokens - 1; x++) {
2282 if ((strcmp(tokens[x].value, "rawcmds") == 0)) {
2283 f |= LOG_RAWCMDS;
2284 } else if ((strcmp(tokens[x].value, "evictions") == 0)) {
2285 f |= LOG_EVICTIONS;
2286 } else if ((strcmp(tokens[x].value, "fetchers") == 0)) {
2287 f |= LOG_FETCHERS;
2288 } else if ((strcmp(tokens[x].value, "mutations") == 0)) {
2289 f |= LOG_MUTATIONS;
2290 } else if ((strcmp(tokens[x].value, "sysevents") == 0)) {
2291 f |= LOG_SYSEVENTS;
2292 } else if ((strcmp(tokens[x].value, "connevents") == 0)) {
2293 f |= LOG_CONNEVENTS;
2294 } else if ((strcmp(tokens[x].value, "proxycmds") == 0)) {
2295 f |= LOG_PROXYCMDS;
2296 } else if ((strcmp(tokens[x].value, "proxyevents") == 0)) {
2297 f |= LOG_PROXYEVENTS;
2298 } else if ((strcmp(tokens[x].value, "proxyuser") == 0)) {
2299 f |= LOG_PROXYUSER;
2300 } else {
2301 out_string(c, "ERROR");
2302 return;
2303 }
2304 }
2305 } else {
2306 f |= LOG_FETCHERS;
2307 }
2308
2309 switch(logger_add_watcher(c, c->sfd, f)) {
2310 case LOGGER_ADD_WATCHER_TOO_MANY:
2311 out_string(c, "WATCHER_TOO_MANY log watcher limit reached");
2312 break;
2313 case LOGGER_ADD_WATCHER_FAILED:
2314 out_string(c, "WATCHER_FAILED failed to add log watcher");
2315 break;
2316 case LOGGER_ADD_WATCHER_OK:
2317 conn_set_state(c, conn_watch);
2318 event_del(&c->event);
2319 break;
2320 }
2321 }
2322
2323 static void process_memlimit_command(conn *c, token_t *tokens, const size_t ntokens) {
2324 uint32_t memlimit;
2325 assert(c != NULL);
2326
2327 set_noreply_maybe(c, tokens, ntokens);
2328
2329 if (!safe_strtoul(tokens[1].value, &memlimit)) {
2330 out_string(c, "ERROR");
2331 } else {
2332 if (memlimit < 8) {
2333 out_string(c, "MEMLIMIT_TOO_SMALL cannot set maxbytes to less than 8m");
2334 } else {
2335 if (memlimit > 1000000000) {
2336 out_string(c, "MEMLIMIT_ADJUST_FAILED input value is megabytes not bytes");
2337 } else if (slabs_adjust_mem_limit((size_t) memlimit * 1024 * 1024)) {
2338 if (settings.verbose > 0) {
2339 fprintf(stderr, "maxbytes adjusted to %llum\n", (unsigned long long)memlimit);
2340 }
2341
2342 out_string(c, "OK");
2343 } else {
2344 out_string(c, "MEMLIMIT_ADJUST_FAILED out of bounds or unable to adjust");
2345 }
2346 }
2347 }
2348 }
2349
2350 static void process_lru_command(conn *c, token_t *tokens, const size_t ntokens) {
2351 uint32_t pct_hot;
2352 uint32_t pct_warm;
2353 double hot_factor;
2354 int32_t ttl;
2355 double factor;
2356
2357 set_noreply_maybe(c, tokens, ntokens);
2358
2359 if (strcmp(tokens[1].value, "tune") == 0 && ntokens >= 7) {
2360 if (!safe_strtoul(tokens[2].value, &pct_hot) ||
2361 !safe_strtoul(tokens[3].value, &pct_warm) ||
2362 !safe_strtod(tokens[4].value, &hot_factor) ||
2363 !safe_strtod(tokens[5].value, &factor)) {
2364 out_string(c, "ERROR");
2365 } else {
2366 if (pct_hot + pct_warm > 80) {
2367 out_string(c, "ERROR hot and warm pcts must not exceed 80");
2368 } else if (factor <= 0 || hot_factor <= 0) {
2369 out_string(c, "ERROR hot/warm age factors must be greater than 0");
2370 } else {
2371 settings.hot_lru_pct = pct_hot;
2372 settings.warm_lru_pct = pct_warm;
2373 settings.hot_max_factor = hot_factor;
2374 settings.warm_max_factor = factor;
2375 out_string(c, "OK");
2376 }
2377 }
2378 } else if (strcmp(tokens[1].value, "mode") == 0 && ntokens >= 4 &&
2379 settings.lru_maintainer_thread) {
2380 if (strcmp(tokens[2].value, "flat") == 0) {
2381 settings.lru_segmented = false;
2382 out_string(c, "OK");
2383 } else if (strcmp(tokens[2].value, "segmented") == 0) {
2384 settings.lru_segmented = true;
2385 out_string(c, "OK");
2386 } else {
2387 out_string(c, "ERROR");
2388 }
2389 } else if (strcmp(tokens[1].value, "temp_ttl") == 0 && ntokens >= 4 &&
2390 settings.lru_maintainer_thread) {
2391 if (!safe_strtol(tokens[2].value, &ttl)) {
2392 out_string(c, "ERROR");
2393 } else {
2394 if (ttl < 0) {
2395 settings.temp_lru = false;
2396 } else {
2397 settings.temp_lru = true;
2398 settings.temporary_ttl = ttl;
2399 }
2400 out_string(c, "OK");
2401 }
2402 } else {
2403 out_string(c, "ERROR");
2404 }
2405 }
2406 #ifdef EXTSTORE
2407 static void process_extstore_command(conn *c, token_t *tokens, const size_t ntokens) {
2408 set_noreply_maybe(c, tokens, ntokens);
2409 bool ok = true;
2410 if (ntokens < 4) {
2411 ok = false;
2412 } else if (strcmp(tokens[1].value, "free_memchunks") == 0 && ntokens > 4) {
2413 /* per-slab-class free chunk setting. */
2414 unsigned int clsid = 0;
2415 unsigned int limit = 0;
2416 if (!safe_strtoul(tokens[2].value, &clsid) ||
2417 !safe_strtoul(tokens[3].value, &limit)) {
2418 ok = false;
2419 } else {
2420 if (clsid < MAX_NUMBER_OF_SLAB_CLASSES) {
2421 settings.ext_free_memchunks[clsid] = limit;
2422 } else {
2423 ok = false;
2424 }
2425 }
2426 } else if (strcmp(tokens[1].value, "item_size") == 0) {
2427 if (!safe_strtoul(tokens[2].value, &settings.ext_item_size))
2428 ok = false;
2429 } else if (strcmp(tokens[1].value, "item_age") == 0) {
2430 if (!safe_strtoul(tokens[2].value, &settings.ext_item_age))
2431 ok = false;
2432 } else if (strcmp(tokens[1].value, "low_ttl") == 0) {
2433 if (!safe_strtoul(tokens[2].value, &settings.ext_low_ttl))
2434 ok = false;
2435 } else if (strcmp(tokens[1].value, "recache_rate") == 0) {
2436 if (!safe_strtoul(tokens[2].value, &settings.ext_recache_rate))
2437 ok = false;
2438 } else if (strcmp(tokens[1].value, "compact_under") == 0) {
2439 if (!safe_strtoul(tokens[2].value, &settings.ext_compact_under))
2440 ok = false;
2441 } else if (strcmp(tokens[1].value, "drop_under") == 0) {
2442 if (!safe_strtoul(tokens[2].value, &settings.ext_drop_under))
2443 ok = false;
2444 } else if (strcmp(tokens[1].value, "max_sleep") == 0) {
2445 if (!safe_strtoul(tokens[2].value, &settings.ext_max_sleep))
2446 ok = false;
2447 } else if (strcmp(tokens[1].value, "max_frag") == 0) {
2448 if (!safe_strtod(tokens[2].value, &settings.ext_max_frag))
2449 ok = false;
2450 } else if (strcmp(tokens[1].value, "drop_unread") == 0) {
2451 unsigned int v;
2452 if (!safe_strtoul(tokens[2].value, &v)) {
2453 ok = false;
2454 } else {
2455 settings.ext_drop_unread = v == 0 ? false : true;
2456 }
2457 } else {
2458 ok = false;
2459 }
2460 if (!ok) {
2461 out_string(c, "ERROR");
2462 } else {
2463 out_string(c, "OK");
2464 }
2465 }
2466 #endif
2467 static void process_flush_all_command(conn *c, token_t *tokens, const size_t ntokens) {
2468 int32_t exptime = 0;
2469 rel_time_t new_oldest = 0;
2470
2471 set_noreply_maybe(c, tokens, ntokens);
2472
2473 pthread_mutex_lock(&c->thread->stats.mutex);
2474 c->thread->stats.flush_cmds++;
2475 pthread_mutex_unlock(&c->thread->stats.mutex);
2476
2477 if (!settings.flush_enabled) {
2478 // flush_all is not allowed but we log it on stats
2479 out_string(c, "CLIENT_ERROR flush_all not allowed");
2480 return;
2481 }
2482
2483 if (ntokens != (c->noreply ? 3 : 2)) {
2484 if (!safe_strtol(tokens[1].value, &exptime)) {
2485 out_string(c, "CLIENT_ERROR invalid exptime argument");
2486 return;
2487 }
2488 }
2489
2490 /*
2491 If exptime is zero realtime() would return zero too, and
2492 realtime(exptime) - 1 would overflow to the max unsigned
2493 value. So we process exptime == 0 the same way we do when
2494 no delay is given at all.
2495 */
2496 if (exptime > 0) {
2497 new_oldest = realtime(exptime);
2498 } else { /* exptime == 0 */
2499 new_oldest = current_time;
2500 }
2501
2502 if (settings.use_cas) {
2503 settings.oldest_live = new_oldest - 1;
2504 if (settings.oldest_live <= current_time)
2505 settings.oldest_cas = get_cas_id();
2506 } else {
2507 settings.oldest_live = new_oldest;
2508 }
2509 out_string(c, "OK");
2510 }
2511
2512 static void process_version_command(conn *c) {
2513 out_string(c, "VERSION " VERSION);
2514 }
2515
2516 static void process_quit_command(conn *c) {
2517 conn_set_state(c, conn_mwrite);
2518 c->close_after_write = true;
2519 c->close_reason = NORMAL_CLOSE;
2520 }
2521
2522 static void process_shutdown_command(conn *c, token_t *tokens, const size_t ntokens) {
2523 if (!settings.shutdown_command) {
2524 out_string(c, "ERROR: shutdown not enabled");
2525 return;
2526 }
2527
2528 if (ntokens == 2) {
2529 c->close_reason = SHUTDOWN_CLOSE;
2530 conn_set_state(c, conn_closing);
2531 raise(SIGINT);
2532 } else if (ntokens == 3 && strcmp(tokens[SUBCOMMAND_TOKEN].value, "graceful") == 0) {
2533 c->close_reason = SHUTDOWN_CLOSE;
2534 conn_set_state(c, conn_closing);
2535 raise(SIGUSR1);
2536 } else {
2537 out_string(c, "CLIENT_ERROR invalid shutdown mode");
2538 }
2539 }
2540
2541 static void process_slabs_command(conn *c, token_t *tokens, const size_t ntokens) {
2542 if (ntokens == 5 && strcmp(tokens[COMMAND_TOKEN + 1].value, "reassign") == 0) {
2543 int src, dst, rv;
2544
2545 if (settings.slab_reassign == false) {
2546 out_string(c, "CLIENT_ERROR slab reassignment disabled");
2547 return;
2548 }
2549
2550 if (! (safe_strtol(tokens[2].value, (int32_t*)&src)
2551 && safe_strtol(tokens[3].value, (int32_t*)&dst))) {
2552 out_string(c, "CLIENT_ERROR bad command line format");
2553 return;
2554 }
2555
2556 rv = slabs_reassign(src, dst);
2557 switch (rv) {
2558 case REASSIGN_OK:
2559 out_string(c, "OK");
2560 break;
2561 case REASSIGN_RUNNING:
2562 out_string(c, "BUSY currently processing reassign request");
2563 break;
2564 case REASSIGN_BADCLASS:
2565 out_string(c, "BADCLASS invalid src or dst class id");
2566 break;
2567 case REASSIGN_NOSPARE:
2568 out_string(c, "NOSPARE source class has no spare pages");
2569 break;
2570 case REASSIGN_SRC_DST_SAME:
2571 out_string(c, "SAME src and dst class are identical");
2572 break;
2573 }
2574 return;
2575 } else if (ntokens >= 4 &&
2576 (strcmp(tokens[COMMAND_TOKEN + 1].value, "automove") == 0)) {
2577 process_slabs_automove_command(c, tokens, ntokens);
2578 } else {
2579 out_string(c, "ERROR");
2580 }
2581 }
2582
2583 static void process_lru_crawler_command(conn *c, token_t *tokens, const size_t ntokens) {
2584 if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "crawl") == 0) {
2585 int rv;
2586 if (settings.lru_crawler == false) {
2587 out_string(c, "CLIENT_ERROR lru crawler disabled");
2588 return;
2589 }
2590
2591 rv = lru_crawler_crawl(tokens[2].value, CRAWLER_EXPIRED, NULL, 0,
2592 settings.lru_crawler_tocrawl);
2593 switch(rv) {
2594 case CRAWLER_OK:
2595 out_string(c, "OK");
2596 break;
2597 case CRAWLER_RUNNING:
2598 out_string(c, "BUSY currently processing crawler request");
2599 break;
2600 case CRAWLER_BADCLASS:
2601 out_string(c, "BADCLASS invalid class id");
2602 break;
2603 case CRAWLER_NOTSTARTED:
2604 out_string(c, "NOTSTARTED no items to crawl");
2605 break;
2606 case CRAWLER_ERROR:
2607 out_string(c, "ERROR an unknown error happened");
2608 break;
2609 }
2610 return;
2611 } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "metadump") == 0) {
2612 if (settings.lru_crawler == false) {
2613 out_string(c, "CLIENT_ERROR lru crawler disabled");
2614 return;
2615 }
2616 if (!settings.dump_enabled) {
2617 out_string(c, "ERROR metadump not allowed");
2618 return;
2619 }
2620 if (resp_has_stack(c)) {
2621 out_string(c, "ERROR cannot pipeline other commands before metadump");
2622 return;
2623 }
2624
2625 int rv = lru_crawler_crawl(tokens[2].value, CRAWLER_METADUMP,
2626 c, c->sfd, LRU_CRAWLER_CAP_REMAINING);
2627 switch(rv) {
2628 case CRAWLER_OK:
2629 // TODO: documentation says this string is returned, but
2630 // it never was before. We never switch to conn_write so
2631 // this o_s call never worked. Need to talk to users and
2632 // decide if removing the OK from docs is fine.
2633 //out_string(c, "OK");
2634 // TODO: Don't reuse conn_watch here.
2635 conn_set_state(c, conn_watch);
2636 event_del(&c->event);
2637 break;
2638 case CRAWLER_RUNNING:
2639 out_string(c, "BUSY currently processing crawler request");
2640 break;
2641 case CRAWLER_BADCLASS:
2642 out_string(c, "BADCLASS invalid class id");
2643 break;
2644 case CRAWLER_NOTSTARTED:
2645 out_string(c, "NOTSTARTED no items to crawl");
2646 break;
2647 case CRAWLER_ERROR:
2648 out_string(c, "ERROR an unknown error happened");
2649 break;
2650 }
2651 return;
2652 } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "tocrawl") == 0) {
2653 uint32_t tocrawl;
2654 if (!safe_strtoul(tokens[2].value, &tocrawl)) {
2655 out_string(c, "CLIENT_ERROR bad command line format");
2656 return;
2657 }
2658 settings.lru_crawler_tocrawl = tocrawl;
2659 out_string(c, "OK");
2660 return;
2661 } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "sleep") == 0) {
2662 uint32_t tosleep;
2663 if (!safe_strtoul(tokens[2].value, &tosleep)) {
2664 out_string(c, "CLIENT_ERROR bad command line format");
2665 return;
2666 }
2667 if (tosleep > 1000000) {
2668 out_string(c, "CLIENT_ERROR sleep must be one second or less");
2669 return;
2670 }
2671 settings.lru_crawler_sleep = tosleep;
2672 out_string(c, "OK");
2673 return;
2674 } else if (ntokens == 3) {
2675 if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "enable") == 0)) {
2676 if (start_item_crawler_thread() == 0) {
2677 out_string(c, "OK");
2678 } else {
2679 out_string(c, "ERROR failed to start lru crawler thread");
2680 }
2681 } else if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "disable") == 0)) {
2682 if (stop_item_crawler_thread(CRAWLER_NOWAIT) == 0) {
2683 out_string(c, "OK");
2684 } else {
2685 out_string(c, "ERROR failed to stop lru crawler thread");
2686 }
2687 } else {
2688 out_string(c, "ERROR");
2689 }
2690 return;
2691 } else {
2692 out_string(c, "ERROR");
2693 }
2694 }
2695 #ifdef TLS
2696 static void process_refresh_certs_command(conn *c, token_t *tokens, const size_t ntokens) {
2697 set_noreply_maybe(c, tokens, ntokens);
2698 char *errmsg = NULL;
2699 if (refresh_certs(&errmsg)) {
2700 out_string(c, "OK");
2701 } else {
2702 write_and_free(c, errmsg, strlen(errmsg));
2703 }
2704 return;
2705 }
2706 #endif
2707
2708 // TODO: pipelined commands are incompatible with shifting connections to a
2709 // side thread. Given this only happens in two instances (watch and
2710 // lru_crawler metadump) it should be fine for things to bail. It _should_ be
2711 // unusual for these commands.
2712 // This is hard to fix since tokenize_command() mutilates the read buffer, so
2713 // we can't drop out and back in again.
2714 // Leaving this note here to spend more time on a fix when necessary, or if an
2715 // opportunity becomes obvious.
2716 void process_command_ascii(conn *c, char *command) {
2717
2718 token_t tokens[MAX_TOKENS];
2719 size_t ntokens;
2720 int comm;
2721
2722 assert(c != NULL);
2723
2724 MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
2725
2726 if (settings.verbose > 1)
2727 fprintf(stderr, "<%d %s\n", c->sfd, command);
2728
2729 /*
2730 * for commands set/add/replace, we build an item and read the data
2731 * directly into it, then continue in nread_complete().
2732 */
2733
2734 // Prep the response object for this query.
2735 if (!resp_start(c)) {
2736 conn_set_state(c, conn_closing);
2737 return;
2738 }
2739
2740 ntokens = tokenize_command(command, tokens, MAX_TOKENS);
2741 // All commands need a minimum of two tokens: cmd and NULL finalizer
2742 // There are also no valid commands shorter than two bytes.
2743 if (ntokens < 2 || tokens[COMMAND_TOKEN].length < 2) {
2744 out_string(c, "ERROR");
2745 return;
2746 }
2747
2748 // Meta commands are all 2-char in length.
2749 char first = tokens[COMMAND_TOKEN].value[0];
2750 if (first == 'm' && tokens[COMMAND_TOKEN].length == 2) {
2751 switch (tokens[COMMAND_TOKEN].value[1]) {
2752 case 'g':
2753 process_mget_command(c, tokens, ntokens);
2754 break;
2755 case 's':
2756 process_mset_command(c, tokens, ntokens);
2757 break;
2758 case 'd':
2759 process_mdelete_command(c, tokens, ntokens);
2760 break;
2761 case 'n':
2762 out_string(c, "MN");
2763 // mn command forces immediate writeback flush.
2764 conn_set_state(c, conn_mwrite);
2765 break;
2766 case 'a':
2767 process_marithmetic_command(c, tokens, ntokens);
2768 break;
2769 case 'e':
2770 process_meta_command(c, tokens, ntokens);
2771 break;
2772 default:
2773 out_string(c, "ERROR");
2774 break;
2775 }
2776 } else if (first == 'g') {
2777 // Various get commands are very common.
2778 WANT_TOKENS_MIN(ntokens, 3);
2779 if (strcmp(tokens[COMMAND_TOKEN].value, "get") == 0) {
2780
2781 process_get_command(c, tokens, ntokens, false, false);
2782 } else if (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0) {
2783
2784 process_get_command(c, tokens, ntokens, true, false);
2785 } else if (strcmp(tokens[COMMAND_TOKEN].value, "gat") == 0) {
2786
2787 process_get_command(c, tokens, ntokens, false, true);
2788 } else if (strcmp(tokens[COMMAND_TOKEN].value, "gats") == 0) {
2789
2790 process_get_command(c, tokens, ntokens, true, true);
2791 } else {
2792 out_string(c, "ERROR");
2793 }
2794 } else if (first == 's') {
2795 if (strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) {
2796
2797 WANT_TOKENS_OR(ntokens, 6, 7);
2798 process_update_command(c, tokens, ntokens, comm, false);
2799 } else if (strcmp(tokens[COMMAND_TOKEN].value, "stats") == 0) {
2800
2801 process_stat(c, tokens, ntokens);
2802 } else if (strcmp(tokens[COMMAND_TOKEN].value, "shutdown") == 0) {
2803
2804 process_shutdown_command(c, tokens, ntokens);
2805 } else if (strcmp(tokens[COMMAND_TOKEN].value, "slabs") == 0) {
2806
2807 process_slabs_command(c, tokens, ntokens);
2808 } else {
2809 out_string(c, "ERROR");
2810 }
2811 } else if (first == 'a') {
2812 if ((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) ||
2813 (strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) ) {
2814
2815 WANT_TOKENS_OR(ntokens, 6, 7);
2816 process_update_command(c, tokens, ntokens, comm, false);
2817 } else {
2818 out_string(c, "ERROR");
2819 }
2820 } else if (first == 'c') {
2821 if (strcmp(tokens[COMMAND_TOKEN].value, "cas") == 0 && (comm = NREAD_CAS)) {
2822
2823 WANT_TOKENS_OR(ntokens, 7, 8);
2824 process_update_command(c, tokens, ntokens, comm, true);
2825 } else if (strcmp(tokens[COMMAND_TOKEN].value, "cache_memlimit") == 0) {
2826
2827 WANT_TOKENS_OR(ntokens, 3, 4);
2828 process_memlimit_command(c, tokens, ntokens);
2829 } else {
2830 out_string(c, "ERROR");
2831 }
2832 } else if (first == 'i') {
2833 if (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0) {
2834
2835 WANT_TOKENS_OR(ntokens, 4, 5);
2836 process_arithmetic_command(c, tokens, ntokens, 1);
2837 } else {
2838 out_string(c, "ERROR");
2839 }
2840 } else if (first == 'd') {
2841 if (strcmp(tokens[COMMAND_TOKEN].value, "delete") == 0) {
2842
2843 WANT_TOKENS(ntokens, 3, 5);
2844 process_delete_command(c, tokens, ntokens);
2845 } else if (strcmp(tokens[COMMAND_TOKEN].value, "decr") == 0) {
2846
2847 WANT_TOKENS_OR(ntokens, 4, 5);
2848 process_arithmetic_command(c, tokens, ntokens, 0);
2849 #ifdef MEMCACHED_DEBUG
2850 } else if (strcmp(tokens[COMMAND_TOKEN].value, "debugtime") == 0) {
2851 WANT_TOKENS_MIN(ntokens, 2);
2852 process_debugtime_command(c, tokens, ntokens);
2853 #endif
2854 } else {
2855 out_string(c, "ERROR");
2856 }
2857 } else if (first == 't') {
2858 if (strcmp(tokens[COMMAND_TOKEN].value, "touch") == 0) {
2859
2860 WANT_TOKENS_OR(ntokens, 4, 5);
2861 process_touch_command(c, tokens, ntokens);
2862 } else {
2863 out_string(c, "ERROR");
2864 }
2865 } else if (
2866 (strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)) ||
2867 (strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) ) {
2868
2869 WANT_TOKENS_OR(ntokens, 6, 7);
2870 process_update_command(c, tokens, ntokens, comm, false);
2871
2872 } else if (strcmp(tokens[COMMAND_TOKEN].value, "bget") == 0) {
2873 // ancient "binary get" command which isn't in any documentation, was
2874 // removed > 10 years ago, etc. Keeping for compatibility reasons but
2875 // we should look deeper into client code and remove this.
2876 WANT_TOKENS_MIN(ntokens, 3);
2877 process_get_command(c, tokens, ntokens, false, false);
2878
2879 } else if (strcmp(tokens[COMMAND_TOKEN].value, "flush_all") == 0) {
2880
2881 WANT_TOKENS(ntokens, 2, 4);
2882 process_flush_all_command(c, tokens, ntokens);
2883
2884 } else if (strcmp(tokens[COMMAND_TOKEN].value, "version") == 0) {
2885
2886 process_version_command(c);
2887
2888 } else if (strcmp(tokens[COMMAND_TOKEN].value, "quit") == 0) {
2889
2890 process_quit_command(c);
2891
2892 } else if (strcmp(tokens[COMMAND_TOKEN].value, "lru_crawler") == 0) {
2893
2894 process_lru_crawler_command(c, tokens, ntokens);
2895
2896 } else if (strcmp(tokens[COMMAND_TOKEN].value, "watch") == 0) {
2897
2898 process_watch_command(c, tokens, ntokens);
2899
2900 } else if (strcmp(tokens[COMMAND_TOKEN].value, "verbosity") == 0) {
2901 WANT_TOKENS_OR(ntokens, 3, 4);
2902 process_verbosity_command(c, tokens, ntokens);
2903 } else if (strcmp(tokens[COMMAND_TOKEN].value, "lru") == 0) {
2904 WANT_TOKENS_MIN(ntokens, 3);
2905 process_lru_command(c, tokens, ntokens);
2906 #ifdef MEMCACHED_DEBUG
2907 // commands which exist only for testing the memcached's security protection
2908 } else if (strcmp(tokens[COMMAND_TOKEN].value, "misbehave") == 0) {
2909 process_misbehave_command(c);
2910 #endif
2911 #ifdef EXTSTORE
2912 } else if (strcmp(tokens[COMMAND_TOKEN].value, "extstore") == 0) {
2913 WANT_TOKENS_MIN(ntokens, 3);
2914 process_extstore_command(c, tokens, ntokens);
2915 #endif
2916 #ifdef TLS
2917 } else if (strcmp(tokens[COMMAND_TOKEN].value, "refresh_certs") == 0) {
2918 process_refresh_certs_command(c, tokens, ntokens);
2919 #endif
2920 } else {
2921 if (strncmp(tokens[ntokens - 2].value, "HTTP/", 5) == 0) {
2922 conn_set_state(c, conn_closing);
2923 } else {
2924 out_string(c, "ERROR");
2925 }
2926 }
2927 return;
2928 }
2929
2930