"Fossies" - the Fresh Open Source Software Archive

Member "memcached-1.6.15/proto_text.c" (30 Mar 2022, 95612 Bytes) of package /linux/www/memcached-1.6.15.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "proto_text.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.6.14_vs_1.6.15.

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