"Fossies" - the Fresh Open Source Software Archive

Member "memcached-1.6.9/proto_text.c" (21 Nov 2020, 89207 Bytes) of package /linux/www/memcached-1.6.9.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.8_vs_1.6.9.

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