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