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