static void asc_process_aggregate(struct conn *c, struct token *token, int ntoken) { int32_t interval; if (ntoken != 4) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } if (!mc_strtol(token[TOKEN_AGGR_COMMAND].val, &interval)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid option '%.*s'", c->sd, c->req_type, token[TOKEN_AGGR_COMMAND].len, token[TOKEN_AGGR_COMMAND].val); asc_write_client_error(c); return; } if (interval > 0) { stats_set_interval(interval); asc_write_ok(c); } else if (interval == 0) { stats_set_interval(STATS_DEFAULT_INTVL); asc_write_ok(c); } else { stats_set_interval(-1000000); asc_write_ok(c); } }
static void asc_process_evict(struct conn *c, struct token *token, int ntoken) { int32_t option; if (ntoken != 4) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } if (!mc_strtol(token[TOKEN_EVICT_COMMAND].val, &option)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid option '%.*s'", c->sd, c->req_type, token[TOKEN_EVICT_COMMAND].len,token[TOKEN_EVICT_COMMAND].val); asc_write_client_error(c); return; } if (option >= EVICT_NONE && option < EVICT_INVALID) { settings.evict_opt = option; asc_write_ok(c); return; } log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid option %"PRId32"", c->sd, c->req_type, option); asc_write_client_error(c); }
static void asc_process_verbosity(struct conn *c, struct token *token, int ntoken) { uint32_t level; asc_set_noreply_maybe(c, token, ntoken); if (ntoken != 3 && ntoken != 4) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } if (!mc_strtoul(token[TOKEN_SUBCOMMAND].val, &level)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid level '%.*s'", c->sd, c->req_type, token[TOKEN_SUBCOMMAND].len, token[TOKEN_SUBCOMMAND].val); asc_write_client_error(c); return; } log_level_set(level); asc_write_ok(c); }
static void asc_process_flushall(struct conn *c, struct token *token, int ntoken) { struct bound *t = &ntoken_bound[REQ_FLUSHALL]; int32_t exptime_int; time_t exptime; time_update(); asc_set_noreply_maybe(c, token, ntoken); if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } if (ntoken == t->b[c->noreply].min) { settings.oldest_live = time_now() - 1; item_flush_expired(); asc_write_ok(c); return; } if (!mc_strtol(token[TOKEN_SUBCOMMAND].val, &exptime_int)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid numeric value '%.*s'", c->sd, c->req_type, token[TOKEN_SUBCOMMAND].len, token[TOKEN_SUBCOMMAND].val); asc_write_client_error(c); return; } exptime = (time_t)exptime_int; /* * If exptime is zero time_reltime() would return zero too, and * time_reltime(exptime) - 1 would overflow to the max unsigned value. * So we process exptime == 0 the same way we do when no delay is * given at all. */ if (exptime > 0) { settings.oldest_live = time_reltime(exptime) - 1; } else { /* exptime == 0 */ settings.oldest_live = time_now() - 1; } item_flush_expired(); asc_write_ok(c); }
static void asc_process_delete(struct conn *c, struct token *token, int ntoken) { char *key; /* key to be deleted */ size_t nkey; /* # key bytes */ struct item *it; /* item for this key */ asc_set_noreply_maybe(c, token, ntoken); if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } key = token[TOKEN_KEY].val; nkey = token[TOKEN_KEY].len; if (nkey > KEY_MAX_LEN) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and %d " "length key", c->sd, c->req_type, nkey); asc_write_client_error(c); return; } /* * FIXME: This is not thread-safe, two threads could try to delete the same * item twice after succeeding in item_get, leading to erroneous stats */ it = item_get(key, nkey); if (it != NULL) { stats_slab_incr(it->id, delete_hit); item_delete(it); asc_write_deleted(c); } else { stats_thread_incr(delete_miss); asc_write_not_found(c); } }
static void asc_process_delete(struct conn *c, struct token *token, int ntoken) { char *key; /* key to be deleted */ size_t nkey; /* # key bytes */ struct item *it; /* item for this key */ asc_set_noreply_maybe(c, token, ntoken); if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_INFO, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } key = token[TOKEN_KEY].val; nkey = token[TOKEN_KEY].len; if (nkey > KEY_MAX_LEN) { log_debug(LOG_INFO, "client error on c %d for req of type %d and %d " "length key", c->sd, c->req_type, nkey); asc_write_client_error(c); return; } it = item_get(key, nkey); if (it != NULL) { stats_slab_incr(it->id, delete_hit); item_unlink(it); item_remove(it); asc_write_deleted(c); } else { stats_thread_incr(delete_miss); asc_write_not_found(c); } }
/* * We get here after reading the value in update commands. The command * is stored in c->req_type, and the item is ready in c->item. */ void asc_complete_nread(struct conn *c) { item_store_result_t ret; struct item *it; char *end; it = c->item; end = item_data(it) + it->nbyte; if (!strcrlf(end)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with missing crlf", c->sd, c->req_type); asc_write_client_error(c); } else { ret = item_store(it, c->req_type, c); switch (ret) { case STORED: asc_write_stored(c); break; case EXISTS: asc_write_exists(c); break; case NOT_FOUND: asc_write_not_found(c); break; case NOT_STORED: asc_write_not_stored(c); break; default: log_warn("server error on c %d for req of type %d with unknown " "store result %d", c->sd, c->req_type, ret); asc_write_server_error(c); break; } } item_remove(c->item); c->item = NULL; }
static void asc_process_stats(struct conn *c, struct token *token, int ntoken) { struct token *t = &token[TOKEN_SUBCOMMAND]; if (!stats_enabled()) { log_warn("server error on c %d for req of type %d because stats is " "disabled", c->sd, c->req_type); asc_write_server_error(c); return; } if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } if (ntoken == 2) { stats_default(c); } else if (strncmp(t->val, "reset", t->len) == 0) { log_warn("server error on c %d for req of type %d because stats reset " "is not supported", c->sd, c->req_type); asc_write_server_error(c); return; } else if (strncmp(t->val, "settings", t->len) == 0) { stats_settings(c); } else if (strncmp(t->val, "cachedump", t->len) == 0) { char *buf; unsigned int bytes, id, limit = 0; if (ntoken < 5) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d " "for req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } if (!mc_strtoul(token[TOKEN_CACHEDUMP_ID].val, &id) || !mc_strtoul(token[TOKEN_CACHEDUMP_LIMIT].val, &limit)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "because either id '%.*s' or limit '%.*s' is invalid", c->sd, c->req_type, token[TOKEN_CACHEDUMP_ID].len, token[TOKEN_CACHEDUMP_ID].val, token[TOKEN_CACHEDUMP_LIMIT].len, token[TOKEN_CACHEDUMP_LIMIT].val); asc_write_client_error(c); return; } if (id < SLABCLASS_MIN_ID || id > SLABCLASS_MAX_ID) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "because %d is an illegal slab id", c->sd, c->req_type, id); asc_write_client_error(c); return; } buf = item_cache_dump(id, limit, &bytes); core_write_and_free(c, buf, bytes); return; } else { /* * Getting here means that the sub command is either engine specific * or is invalid. query the engine and see */ if (strncmp(t->val, "slabs", t->len) == 0) { stats_slabs(c); } else if (strncmp(t->val, "sizes", t->len) == 0) { stats_sizes(c); } else { log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid stats subcommand '%.*s", c->sd, c->req_type, t->len, t->val); asc_write_client_error(c); return; } if (c->stats.buffer == NULL) { log_warn("server error on c %d for req of type %d because of oom " "writing stats", c->sd, c->req_type); asc_write_server_error(c); } else { core_write_and_free(c, c->stats.buffer, c->stats.offset); c->stats.buffer = NULL; } return; } /* append terminator and start the transfer */ stats_append(c, NULL, 0, NULL, 0); if (c->stats.buffer == NULL) { log_warn("server error on c %d for req of type %d because of oom " "writing stats", c->sd, c->req_type); asc_write_server_error(c); } else { core_write_and_free(c, c->stats.buffer, c->stats.offset); c->stats.buffer = NULL; } }
static void asc_process_arithmetic(struct conn *c, struct token *token, int ntoken) { item_delta_result_t res; char temp[INCR_MAX_STORAGE_LEN]; uint64_t delta; char *key; size_t nkey; bool incr; asc_set_noreply_maybe(c, token, ntoken); if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } incr = (c->req_type == REQ_INCR) ? true : false; key = token[TOKEN_KEY].val; nkey = token[TOKEN_KEY].len; if (nkey > KEY_MAX_LEN) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and %d " "length key", c->sd, c->req_type, nkey); asc_write_client_error(c); return; } if (!mc_strtoull(token[TOKEN_DELTA].val, &delta)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid delta '%.*s'", c->sd, c->req_type, token[TOKEN_DELTA].len, token[TOKEN_DELTA].val); asc_write_client_error(c); return; } res = item_add_delta(c, key, nkey, incr, delta, temp); switch (res) { case DELTA_OK: asc_write_string(c, temp, strlen(temp)); klog_write(c->peer, c->req_type, c->req, c->req_len, res, strlen(temp)); break; case DELTA_NON_NUMERIC: log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "non-numeric value", c->sd, c->req_type); asc_write_client_error(c); break; case DELTA_EOM: log_warn("server error on c %d for req of type %d because of oom", c->sd, c->req_type); asc_write_server_error(c); break; case DELTA_NOT_FOUND: if (incr) { stats_thread_incr(incr_miss); } else { stats_thread_incr(decr_miss); } asc_write_not_found(c); break; default: NOT_REACHED(); break; } }
static void asc_process_update(struct conn *c, struct token *token, int ntoken) { char *key; size_t nkey; uint32_t flags, vlen; int32_t exptime_int; time_t exptime; uint64_t req_cas_id = 0; struct item *it; bool handle_cas; req_type_t type; uint8_t id; asc_set_noreply_maybe(c, token, ntoken); if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } type = c->req_type; handle_cas = (type == REQ_CAS) ? true : false; key = token[TOKEN_KEY].val; nkey = token[TOKEN_KEY].len; if (nkey > KEY_MAX_LEN) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and %d " "length key", c->sd, c->req_type, nkey); asc_write_client_error(c); return; } if (!mc_strtoul(token[TOKEN_FLAGS].val, &flags)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and " "invalid flags '%.*s'", c->sd, c->req_type, token[TOKEN_FLAGS].len, token[TOKEN_FLAGS].val); asc_write_client_error(c); return; } if (!mc_strtol(token[TOKEN_EXPIRY].val, &exptime_int)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and " "invalid expiry '%.*s'", c->sd, c->req_type, token[TOKEN_EXPIRY].len, token[TOKEN_EXPIRY].val); asc_write_client_error(c); return; } if (!mc_strtoul(token[TOKEN_VLEN].val, &vlen)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and " "invalid vlen '%.*s'", c->sd, c->req_type, token[TOKEN_VLEN].len, token[TOKEN_VLEN].val); asc_write_client_error(c); return; } id = item_slabid(nkey, vlen); if (id == SLABCLASS_INVALID_ID) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and " "slab id out of range for key size %"PRIu8" and value size " "%"PRIu32, c->sd, c->req_type, nkey, vlen); asc_write_client_error(c); return; } exptime = (time_t)exptime_int; /* does cas value exist? */ if (handle_cas) { if (!mc_strtoull(token[TOKEN_CAS].val, &req_cas_id)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d and " "invalid cas '%.*s'", c->sd, c->req_type, token[TOKEN_CAS].len, token[TOKEN_CAS].val); asc_write_client_error(c); return; } } it = item_alloc(id, key, nkey, flags, time_reltime(exptime), vlen); if (it == NULL) { log_warn("server error on c %d for req of type %d because of oom in " "storing item", c->sd, c->req_type); asc_write_server_error(c); /* swallow the data line */ c->write_and_go = CONN_SWALLOW; c->sbytes = vlen + CRLF_LEN; /* * Avoid stale data persisting in cache because we failed alloc. * Unacceptable for SET. Anywhere else too? * * FIXME: either don't delete anything or should be unacceptable for * all but add. */ if (type == REQ_SET) { it = item_get(key, nkey); if (it != NULL) { item_delete(it); } } return; } item_set_cas(it, req_cas_id); c->item = it; c->ritem = item_data(it); c->rlbytes = it->nbyte + CRLF_LEN; conn_set_state(c, CONN_NREAD); }
static inline void asc_process_read(struct conn *c, struct token *token, int ntoken) { rstatus_t status; char *key; size_t nkey; unsigned valid_key_iter = 0; struct item *it; struct token *key_token; bool return_cas; if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } return_cas = (c->req_type == REQ_GETS) ? true : false; key_token = &token[TOKEN_KEY]; do { while (key_token->len != 0) { key = key_token->val; nkey = key_token->len; if (nkey > KEY_MAX_LEN) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "and %d length key", c->sd, c->req_type, nkey); asc_write_client_error(c); return; } if (return_cas) { stats_thread_incr(gets); } else { stats_thread_incr(get); } it = item_get(key, nkey); if (it != NULL) { /* item found */ if (return_cas) { stats_slab_incr(it->id, gets_hit); } else { stats_slab_incr(it->id, get_hit); } if (valid_key_iter >= c->isize) { struct item **new_list; new_list = mc_realloc(c->ilist, sizeof(struct item *) * c->isize * 2); if (new_list != NULL) { c->isize *= 2; c->ilist = new_list; } else { item_remove(it); break; } } status = asc_respond_get(c, valid_key_iter, it, return_cas); if (status != MC_OK) { log_debug(LOG_NOTICE, "client error on c %d for req of type " "%d with %d tokens", c->sd, c->req_type, ntoken); stats_thread_incr(cmd_error); item_remove(it); break; } log_debug(LOG_VVERB, ">%d sending key %.*s", c->sd, it->nkey, item_key(it)); item_touch(it); *(c->ilist + valid_key_iter) = it; valid_key_iter++; } else { /* item not found */ if (return_cas) { stats_thread_incr(gets_miss); } else { stats_thread_incr(get_miss); } klog_write(c->peer, c->req_type, key, nkey, 1, 0); } key_token++; } /* * If the command string hasn't been fully processed, get the next set * of token. */ if (key_token->val != NULL) { ntoken = asc_tokenize(key_token->val, token, TOKEN_MAX); /* ntoken is unused */ key_token = token; } } while (key_token->val != NULL); c->icurr = c->ilist; c->ileft = valid_key_iter; if (return_cas) { c->scurr = c->slist; c->sleft = valid_key_iter; } log_debug(LOG_VVERB, ">%d END", c->sd); /* * If the loop was terminated because of out-of-memory, it is not * reliable to add END\r\n to the buffer, because it might not end * in \r\n. So we send SERVER_ERROR instead. */ if (key_token->val != NULL || conn_add_iov(c, "END\r\n", 5) != MC_OK || (c->udp && conn_build_udp_headers(c) != MC_OK)) { log_warn("server error on c %d for req of type %d with enomem", c->sd, c->req_type); asc_write_server_error(c); } else { conn_set_state(c, CONN_MWRITE); c->msg_curr = 0; } }
static void asc_dispatch(struct conn *c) { rstatus_t status; struct token token[TOKEN_MAX]; int ntoken; /* * For commands set, add, or replace, we build an item and read the data * directly into it, then continue in asc_complete_nread(). */ c->msg_curr = 0; c->msg_used = 0; c->iov_used = 0; status = conn_add_msghdr(c); if (status != MC_OK) { log_warn("server error on c %d for req of type %d because of oom in " "preparing response", c->sd, c->req_type); asc_write_server_error(c); return; } ntoken = asc_tokenize(c->req, token, TOKEN_MAX); c->req_type = asc_parse_type(c, token, ntoken); switch (c->req_type) { case REQ_GET: case REQ_GETS: /* we do not update stats metrics here because of multi-get */ asc_process_read(c, token, ntoken); break; case REQ_SET: stats_thread_incr(set); asc_process_update(c, token, ntoken); break; case REQ_ADD: stats_thread_incr(add); asc_process_update(c, token, ntoken); break; case REQ_REPLACE: stats_thread_incr(replace); asc_process_update(c, token, ntoken); break; case REQ_APPEND: stats_thread_incr(append); asc_process_update(c, token, ntoken); break; case REQ_PREPEND: stats_thread_incr(prepend); asc_process_update(c, token, ntoken); break; case REQ_CAS: stats_thread_incr(cas); asc_process_update(c, token, ntoken); break; case REQ_INCR: stats_thread_incr(incr); asc_process_arithmetic(c, token, ntoken); break; case REQ_DECR: stats_thread_incr(decr); asc_process_arithmetic(c, token, ntoken); break; case REQ_DELETE: stats_thread_incr(delete); asc_process_delete(c, token, ntoken); break; case REQ_STATS: stats_thread_incr(stats); asc_process_stats(c, token, ntoken); break; case REQ_FLUSHALL: stats_thread_incr(flush); asc_process_flushall(c, token, ntoken); break; case REQ_VERSION: asc_write_version(c); break; case REQ_QUIT: conn_set_state(c, CONN_CLOSE); break; case REQ_VERBOSITY: asc_process_verbosity(c, token, ntoken); break; case REQ_CONFIG: asc_process_config(c, token, ntoken); break; case REQ_UNKNOWN: default: log_hexdump(LOG_INFO, c->req, c->req_len, "req on c %d with %d " "invalid tokens", c->sd, ntoken); asc_write_client_error(c); break; } }
static void asc_process_klog(struct conn *c, struct token *token, int ntoken) { struct token *t; if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } t = &token[TOKEN_KLOG_COMMAND]; if (strncmp(t->val, "run", t->len) == 0) { if (settings.klog_name == NULL) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "with klog filename not set", c->sd, c->req_type); asc_write_client_error(c); return; } t = &token[TOKEN_KLOG_SUBCOMMAND]; if (strncmp(t->val, "start", t->len) == 0) { log_debug(LOG_NOTICE, "klog start at epoch %u", time_now()); settings.klog_running = true; asc_write_ok(c); } else if (strncmp(t->val, "stop", t->len) == 0) { log_debug(LOG_NOTICE, "klog stops at epoch %u", time_now()); settings.klog_running = false; asc_write_ok(c); } else { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "with invalid klog run subcommand '%.*s'", c->sd, c->req_type, t->len, t->val); asc_write_client_error(c); } } else if (strncmp(t->val, "interval", t->len) == 0) { t = &token[TOKEN_KLOG_SUBCOMMAND]; if (strncmp(t->val, "reset", t->len) == 0) { stats_set_interval(STATS_DEFAULT_INTVL); asc_write_ok(c); } else { int32_t interval; if (!mc_strtol(t->val, &interval)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "with invalid klog interval '%.*s'", c->sd, c->req_type, t->len, t->val); asc_write_client_error(c); } else if (interval < KLOG_MIN_INTVL) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "with invalid klog interval %"PRId32"", c->sd, c->req_type, interval); asc_write_client_error(c); } else { stats_set_interval(interval); asc_write_ok(c); } } } else if (strncmp(t->val, "sampling", t->len) == 0) { t = &token[TOKEN_KLOG_SUBCOMMAND]; if (strncmp(t->val, "reset", t->len) == 0) { settings.klog_sampling_rate = KLOG_DEFAULT_SMP_RATE; asc_write_ok(c); } else { int32_t sampling; if (!mc_strtol(t->val, &sampling)) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "with invalid klog sampling '%.*s'", c->sd, c->req_type, t->len, t->val); asc_write_client_error(c); } else if (sampling <= 0) { log_debug(LOG_NOTICE, "client error on c %d for req of type %d " "with invalid klog sampling %"PRId32"", c->sd, c->req_type, sampling); asc_write_client_error(c); } else { settings.klog_sampling_rate = sampling; asc_write_ok(c); } } } else { log_debug(LOG_NOTICE, "client error on c %d for req of type %d with " "invalid klog subcommand '%.*s'", c->sd, c->req_type, t->len, t->val); asc_write_client_error(c); } }
static void asc_process_update(struct conn *c, struct token *token, int ntoken) { char *key; size_t nkey; unsigned int flags; int32_t exptime_int; time_t exptime; int vlen; uint64_t req_cas_id = 0; struct item *it; bool handle_cas; req_type_t type; asc_set_noreply_maybe(c, token, ntoken); if (!asc_ntoken_valid(c, ntoken)) { log_hexdump(LOG_INFO, c->req, c->req_len, "client error on c %d for " "req of type %d with %d invalid tokens", c->sd, c->req_type, ntoken); asc_write_client_error(c); return; } type = c->req_type; handle_cas = (type == REQ_CAS) ? true : false; key = token[TOKEN_KEY].val; nkey = token[TOKEN_KEY].len; if (nkey > KEY_MAX_LEN) { log_debug(LOG_INFO, "client error on c %d for req of type %d and %d " "length key", c->sd, c->req_type, nkey); asc_write_client_error(c); return; } if (!mc_strtoul(token[TOKEN_FLAGS].val, (uint32_t *)&flags)) { log_debug(LOG_INFO, "client error on c %d for req of type %d and " "invalid flags '%.*s'", c->sd, c->req_type, token[TOKEN_FLAGS].len, token[TOKEN_FLAGS].val); asc_write_client_error(c); return; } if (!mc_strtol(token[TOKEN_EXPIRY].val, &exptime_int)) { log_debug(LOG_INFO, "client error on c %d for req of type %d and " "invalid expiry '%.*s'", c->sd, c->req_type, token[TOKEN_EXPIRY].len, token[TOKEN_EXPIRY].val); asc_write_client_error(c); return; } if (!mc_strtol(token[TOKEN_VLEN].val, (int32_t *)&vlen)) { log_debug(LOG_INFO, "client error on c %d for req of type %d and " "invalid vlen '%.*s'", c->sd, c->req_type, token[TOKEN_VLEN].len, token[TOKEN_VLEN].val); asc_write_client_error(c); return; } exptime = (time_t)exptime_int; /* does cas value exist? */ if (handle_cas) { if (!mc_strtoull(token[TOKEN_CAS].val, &req_cas_id)) { log_debug(LOG_INFO, "client error on c %d for req of type %d and " "invalid cas '%.*s'", c->sd, c->req_type, token[TOKEN_CAS].len, token[TOKEN_CAS].val); asc_write_client_error(c); return; } } if (vlen < 0) { log_debug(LOG_INFO, "client error on c %d for req of type %d and " "invalid vlen %d", c->sd, c->req_type, vlen); asc_write_client_error(c); return; } vlen += CRLF_LEN; it = item_alloc(key, nkey, flags, time_reltime(exptime), vlen); if (it == NULL) { log_debug(LOG_DEBUG, "server error on c %d for req of type %d because " "of oom in storing item", c->sd, c->req_type); asc_write_server_error(c); /* swallow the data line */ c->write_and_go = CONN_SWALLOW; c->sbytes = vlen; /* * Avoid stale data persisting in cache because we failed alloc. * Unacceptable for SET. Anywhere else too? */ if (type == REQ_SET) { it = item_get(key, nkey); if (it != NULL) { item_unlink(it); item_remove(it); } } return; } item_set_cas(it, req_cas_id); c->item = it; c->ritem = item_data(it); c->rlbytes = it->nbyte; conn_set_state(c, CONN_NREAD); }