// atomically decrement the refcount using __sync_add_and_fetch // to get the value after the decrement. If the value drops // to zero free the msg void mc_msg_decref(mc_msg_t* msg) { FBI_ASSERT(msg != NULL); if (msg->_refcount != MSG_NOT_REFCOUNTED) { FBI_ASSERT(msg->_refcount > 0); int new_refcount; if (_mc_msg_use_atomic_refcounts) { new_refcount = __sync_add_and_fetch(&msg->_refcount, -1); } else { #ifndef FBCODE_OPT_BUILD FBI_ASSERT(__sync_bool_compare_and_swap(&msg->_refcount, msg->_refcount, msg->_refcount - 1) == 1); new_refcount = msg->_refcount; #else new_refcount = --msg->_refcount; #endif } if (new_refcount == 0) { if (_mc_msg_track_num_outstanding) { __sync_fetch_and_add(&_mc_msg_num_outstanding, -1); } #ifndef LIBMC_FBTRACE_DISABLE mc_fbtrace_info_decref(msg->fbtrace_info); #endif #ifndef FBCODE_OPT_BUILD memset(msg, 'P', sizeof(*msg)); #endif free(msg); } } }
int mc_msg_grow(mc_msg_t **msg_ptr, size_t len, void **field_ptr) { FBI_ASSERT(msg_ptr != NULL && field_ptr != NULL); mc_msg_t *msg = *msg_ptr; // assert that the field_ptr refers to something within the *msg FBI_ASSERT(_in_msg(msg, field_ptr, sizeof(*field_ptr))); size_t field_ptr_offset = (void*)field_ptr - (void*)msg; size_t field_offset = sizeof(*msg) + msg->_extra_size; mc_msg_t *new_msg = mc_msg_realloc(msg, msg->_extra_size + len); if (new_msg == NULL) { return -1; } msg = new_msg; void **new_field_ptr = (void**) ((void*)msg + field_ptr_offset); FBI_ASSERT((void*)field_ptr-(void*)(*msg_ptr) == (void*)new_field_ptr-(void*)msg); *new_field_ptr = ((void*)msg) + field_offset; *msg_ptr = msg; return 0; }
static void mc_fbtrace_decref(mc_fbtrace_t* fbt) { FBI_ASSERT(fbt); int newrefcount = __sync_add_and_fetch(&fbt->_refcount, -1); FBI_ASSERT(newrefcount >= 0); if (newrefcount == 0) { free(fbt); } }
ssize_t um_consume_one_message(um_parser_t* um_parser, const uint8_t* buf, size_t nbuf, uint64_t* reqid_out, mc_msg_t** msg_out) { FBI_ASSERT(um_parser && buf && nbuf > 0 && reqid_out && msg_out); *msg_out = NULL; ssize_t consumed = entry_list_preparer_read(&um_parser->prep, (const char*)buf, nbuf); if (consumed <= 0) { goto error; } /* Because the rank of the unsigned integer is equal to the rank of the * signed integer, the signed integer is converted to the type of the * unsigned integer, and this assertion triggers on error. To get around * this, we do the cast ourselves. */ FBI_ASSERT(consumed <= (ssize_t)nbuf); if (entry_list_preparer_finished(&um_parser->prep)) { entry_list_t elist; if (entry_list_consume_preparer(&elist, &um_parser->prep) < 0) { goto error; } mc_msg_t base; mc_msg_init_not_refcounted(&base); _parse_info_t parse_info; if (_fill_base_msg(&elist, &base, &parse_info) != 0) { goto error; } *reqid_out = parse_info.reqid; *msg_out = _msg_create(&base, &elist, &parse_info); if (*msg_out == NULL) { dbg_error("msg_create failed"); goto error; } FBI_ASSERT((*msg_out)->op != mc_op_end); entry_list_cleanup(&elist); } else { FBI_ASSERT(consumed == nbuf); } return consumed; error: entry_list_preparer_reset_after_failure(&um_parser->prep); // Function that return an error must have not left any messages around FBI_ASSERT(*msg_out == NULL); return -1; }
mc_msg_t* mc_msg_dup(const mc_msg_t *msg) { FBI_ASSERT(msg); FBI_ASSERT(msg->_refcount > 0 || msg->_refcount == MSG_NOT_REFCOUNTED); mc_msg_t *msg_copy = mc_msg_new(msg->_extra_size); if (msg_copy == NULL) { return NULL; } _msgcpy(msg_copy, msg); return msg_copy; }
/* Return 1 if pointer p lies within mc_msg_t msg's body * 0 if pointer p to p + len does not overlap at all * assert that p <-> p + len does not partially overlap */ static inline int _in_msg(const mc_msg_t *msg, void *p, size_t n) { FBI_ASSERT(msg); void *end = (void*)msg + sizeof(mc_msg_t) + msg->_extra_size; if (p >= (void*)msg && p < end) { FBI_ASSERT(p + n <= end); return 1; } FBI_ASSERT(p + n <= (void*)msg || p > end); // No partial overlap return 0; }
/* copy src's contents into dst * dst->_extra_size must be greater than or equal to src->_extra_size * If any string fields occur within src's whole message, they will be * deep copied into dst's deep message. External strings will be shallow * copied */ static void _msgcpy(mc_msg_t *dst, const mc_msg_t *src) { FBI_ASSERT(dst && (dst->_refcount > 0 || dst->_refcount == MSG_NOT_REFCOUNTED)); FBI_ASSERT(src && (src->_refcount > 0 || src->_refcount == MSG_NOT_REFCOUNTED)); FBI_ASSERT(dst->_extra_size >= src->_extra_size); // The difference between the old msg and the new message // By adding this to void*'s we can shift the pointer forward pretty easily // // Example: // ________(delta)_________ // | | // v v // | src ... | | dst ... | // ^ ^ // ptr ptr // |________(delta)_________| // // dst->ptr = (void*)src->ptr + delta ssize_t delta = (void*)dst - (void*)src; int _refcount = dst->_refcount; size_t _extra_size = dst->_extra_size; memcpy(dst, src, sizeof(mc_msg_t) + src->_extra_size); dst->_refcount = _refcount; // restore dst->_extra_size = _extra_size; // restore #ifndef LIBMC_FBTRACE_DISABLE if (src->fbtrace_info) { dst->fbtrace_info = mc_fbtrace_info_deep_copy(src->fbtrace_info); } #endif if (src->stats != NULL) { int i, stats_count = src->number * 2; FBI_ASSERT(stats_count > 0); if (_in_msg(src, src->stats, sizeof(src->stats[0]) * stats_count)) { dst->stats = (void*)(src->stats) + delta; for (i = 0; i < stats_count; i++) { if (_in_msg(src, src->stats[i].str, src->stats[i].len)) { dst->stats[i].str = (void*)(src->stats[i].str) + delta; } } } } if (_in_msg(src, src->key.str, src->key.len)) { dst->key.str = (void*)(src->key.str) + delta; } if (_in_msg(src, src->value.str, src->value.len)) { dst->value.str = (void*)(src->value.str) + delta; } }
static char* stats_reply_to_string(nstring_t *stats, size_t num_stats, size_t *len) { uint64_t i; size_t slen = 0; size_t off = 0; char *s = NULL; for (i = 0; i < num_stats; i++) { nstring_t *name = &stats[2 * i]; nstring_t *value = &stats[2 * i + 1]; // At least one of them should have non-zero length FBI_ASSERT(name->len || value->len); // Not as naive as it looks, the compiler precomputes strlen for constants slen += strlen("STAT " /* name\s?value */ ASCII_TERM); slen += name->len; slen += value->len; if (name->len > 0 && value->len > 0) slen++; // space between them } slen += strlen("END\r\n"); slen += 1; // '\0' s = malloc(slen); if (s == NULL) { return s; } for (i = 0; i < num_stats; i++) { nstring_t *name = &stats[2 * i]; nstring_t *value = &stats[2 * i + 1]; off += snprintf(s + off, slen - off, "STAT"); if (name->len) { off += snprintf(s+off, slen-off, " %.*s", (int) name->len, name->str); } if (value->len) { off += snprintf(s+off, slen-off, " %.*s", (int) value->len, value->str); } memcpy(s+off, ASCII_TERM, strlen(ASCII_TERM)); off += strlen(ASCII_TERM); FBI_ASSERT(off < slen); } FBI_ASSERT(slen - off == strlen("END\r\n") + 1); snprintf(s + off, slen - off, "END\r\n"); *len = slen - 1; return s; }
void ProxyThread::proxyThreadRun() { FBI_ASSERT(proxy->router != nullptr); mcrouter_set_thread_name(pthread_self(), proxy->router->opts, "mcrpxy"); while(!proxy->router->shutdownStarted()) { mcrouterLoopOnce(proxy->eventBase); } while (proxy->fiberManager.hasTasks()) { mcrouterLoopOnce(proxy->eventBase); } proxy->stopAwriterThreads(); // Delete the proxy from the proxy thread so that the clients get // deleted from the same thread where they were created. folly::EventBase *eventBase = proxy->eventBase; std::unique_lock<std::mutex> lk(mux); // This is to avoid a race condition where proxy is deleted // before the call to stopAndJoin is made. cv.wait(lk, [this]() { return this->isSafeToDeleteProxy; }); delete proxy; if (eventBase != nullptr) { delete eventBase; } }
int dynamic_stats_unregister(dynamic_stat_t* stat_ptr) { FBI_ASSERT(stat_ptr); FBI_ASSERT(dynamic_stats_initialized); { std::lock_guard<std::mutex> guard(dynamic_stats_mutex()); TAILQ_REMOVE(&all_dynamic_stats, stat_ptr, entry); } if (stat_ptr->stat.type == stat_string) { free(stat_ptr->stat.data.string); } free(stat_ptr); return 0; }
ProxyRequestContext::ProxyRequestContext( proxy_request_t* preq, std::shared_ptr<const ProxyConfigIf> config) : preq_(proxy_request_incref(preq)), config_(std::move(config)) { FBI_ASSERT(preq_); }
// atomically increment the refcount using __sync_fetch_and_add mc_msg_t* mc_msg_incref(mc_msg_t* msg) { FBI_ASSERT(msg != NULL); if (msg->_refcount != MSG_NOT_REFCOUNTED) { FBI_ASSERT(msg->_refcount >= 0); if (_mc_msg_use_atomic_refcounts) { __sync_fetch_and_add(&msg->_refcount, 1); } else { #ifndef FBCODE_OPT_BUILD FBI_ASSERT(__sync_bool_compare_and_swap(&msg->_refcount, msg->_refcount, msg->_refcount + 1) == 1); #else msg->_refcount++; #endif } } return msg; }
mc_fbtrace_info_t* mc_fbtrace_info_incref(mc_fbtrace_info_t* fbt_i) { if (!fbt_i) { return NULL; } int newrefcount = __sync_add_and_fetch(&fbt_i->_refcount, 1); FBI_ASSERT(newrefcount > 0); return fbt_i; }
void DestinationClient::initializeAsyncMcClient() { FBI_ASSERT(proxy_->eventBase); auto pdstn = pdstn_.lock(); assert(pdstn != nullptr); ConnectionOptions options(pdstn->accessPoint); options.noNetwork = proxy_->opts.no_network; options.tcpKeepAliveCount = proxy_->opts.keepalive_cnt; options.tcpKeepAliveIdle = proxy_->opts.keepalive_idle_s; options.tcpKeepAliveInterval = proxy_->opts.keepalive_interval_s; options.timeout = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::seconds(pdstn->server_timeout.tv_sec) + std::chrono::microseconds(pdstn->server_timeout.tv_usec)); if (proxy_->opts.enable_qos) { options.enableQoS = true; options.qos = pdstn->qos; } if (pdstn->use_ssl) { auto& opts = proxy_->opts; checkLogic(!opts.pem_cert_path.empty() && !opts.pem_key_path.empty() && !opts.pem_ca_path.empty(), "Some of ssl key paths are not set!"); options.sslContextProvider = [&opts] { return getSSLContext(opts.pem_cert_path, opts.pem_key_path, opts.pem_ca_path); }; } asyncMcClient_ = folly::make_unique<AsyncMcClient>(*proxy_->eventBase, std::move(options)); auto pdstnWeakPtr = pdstn_; asyncMcClient_->setStatusCallbacks( [pdstnWeakPtr] () { auto pdstnPtr = pdstnWeakPtr.lock(); if (!pdstnPtr) { return; } pdstnPtr->on_up(); }, [pdstnWeakPtr] (const apache::thrift::transport::TTransportException&) { auto pdstnPtr = pdstnWeakPtr.lock(); if (!pdstnPtr) { return; } pdstnPtr->on_down(); }); if (proxy_->opts.target_max_inflight_requests > 0) { asyncMcClient_->setThrottle(proxy_->opts.target_max_inflight_requests, proxy_->opts.target_max_pending_requests); } }
mc_msg_t* mc_msg_dup_append_key_full(const mc_msg_t *msg, const char* key_append, size_t nkey_append) { FBI_ASSERT(msg); FBI_ASSERT(msg->_refcount > 0 || msg->_refcount == MSG_NOT_REFCOUNTED); FBI_ASSERT(key_append); // Stats are not supported. if (msg->stats) { return NULL; } if (!nkey_append) { return mc_msg_dup(msg); } size_t new_extra_size = msg->_extra_size + nkey_append; if (!_in_msg(msg, msg->key.str, msg->key.len)) { new_extra_size += msg->key.len + 1; // +1 for null terminator } mc_msg_t* const msg_copy = mc_msg_new(new_extra_size); if (msg_copy == NULL) { return NULL; } mc_msg_shallow_copy(msg_copy, msg); // The new message's key is always embedded. msg_copy->key.len = msg->key.len + nkey_append; msg_copy->key.str = (void*) msg_copy + sizeof(*msg_copy); memcpy(msg_copy->key.str, msg->key.str, msg->key.len); memcpy(msg_copy->key.str + msg->key.len, key_append, nkey_append); msg_copy->key.str[msg_copy->key.len] = 0; if (_in_msg(msg, msg->value.str, msg->value.len)) { // The value starts after the key, including the null terminator. msg_copy->value.str = msg_copy->key.str + msg_copy->key.len + 1; memcpy(msg_copy->value.str, msg->value.str, msg_copy->value.len); } return msg_copy; }
void mc_msg_nzlib_compress(mc_msg_t **msgP) { FBI_ASSERT(msgP && *msgP); mc_msg_t *msg = *msgP; nzlib_format_t *format = NULL; size_t len = 0; int rc; if (msg->value.str == NULL || msg->value.len <= 0 || (msg->flags & MC_MSG_FLAG_NZLIB_COMPRESSED)) { goto epilogue; } // Get the upper bound on the deflated value length len = compressBound(msg->value.len); format = malloc(sizeof(*format) + len); if (format == NULL) { goto epilogue; } format->magic = htonl(NZLIB_MAGIC); format->decompressed_sz = htonl(msg->value.len); rc = compress(format->buf, &len, (uint8_t*)msg->value.str, msg->value.len); if (rc != Z_OK) { goto epilogue; } len += sizeof(*format); if (len >= msg->value.len) { goto epilogue; } // If msg->value is not in the msg, grow the msg so we don't trample // someone else's value buffer if (!_in_msg(msg, msg->value.str, msg->value.len) && mc_msg_grow(&msg, len + 1, (void**) &msg->value.str) != 0) { goto epilogue; } msg->value.len = len; memcpy(msg->value.str, format, len); msg->value.str[len] = '\0'; msg->flags |= MC_MSG_FLAG_NZLIB_COMPRESSED; *msgP = msg; epilogue: if (format) { free(format); } }
static void _backing_msg_fill(um_backing_msg_t* bmsg, uint64_t reqid, mc_msg_t* msg, struct iovec* value_iovs, size_t n_value_iovs) { FBI_ASSERT(bmsg->msg == NULL); if (msg->_refcount != MSG_NOT_REFCOUNTED) { bmsg->msg = mc_msg_incref(msg); } else { bmsg->msg = NULL; } _msg_to_elist(&bmsg->elist, reqid, msg, value_iovs, n_value_iovs); bmsg->inuse = 1; }
void mc_fbtrace_info_decref(mc_fbtrace_info_t* fbt_i) { if (!fbt_i) { return; } int newrefcount = __sync_add_and_fetch(&fbt_i->_refcount, -1); FBI_ASSERT(newrefcount >= 0); if (newrefcount == 0) { if (fbt_i->fbtrace) { mc_fbtrace_decref(fbt_i->fbtrace); } free(fbt_i); } }
void mc_msg_shallow_copy(mc_msg_t *dst, const mc_msg_t *src) { FBI_ASSERT(dst && src); int _refcount = dst->_refcount; size_t _extra_size = dst->_extra_size; *dst = *src; dst->_refcount = _refcount; dst->_extra_size = _extra_size; #ifndef LIBMC_FBTRACE_DISABLE if (src->fbtrace_info) { dst->fbtrace_info = mc_fbtrace_info_deep_copy(src->fbtrace_info); } #endif }
dynamic_stat_t* dynamic_stats_register(const stat_t* stat, void* ptr) { // Check for unique name? FBI_ASSERT(dynamic_stats_initialized); dynamic_stat_t* ret = (dynamic_stat_t*) malloc(sizeof(dynamic_stat_t)); if (ret != nullptr) { memcpy(&ret->stat, stat, sizeof(stat_t)); ret->entity_ptr = ptr; { std::lock_guard<std::mutex> guard(dynamic_stats_mutex()); TAILQ_INSERT_TAIL(&all_dynamic_stats, ret, entry); } } return ret; }
int um_consume_buffer(um_parser_t* um_parser, const uint8_t* buf, size_t nbuf, msg_ready_cb* msg_ready, void* context) { while (nbuf > 0) { uint64_t reqid; mc_msg_t* msg; ssize_t consumed = um_consume_one_message(um_parser, buf, nbuf, &reqid, &msg); if (consumed <= 0) { return -1; } FBI_ASSERT(consumed <= nbuf); if (msg != NULL) { msg_ready(context, reqid, msg); buf += consumed; nbuf -= consumed; } else { FBI_ASSERT(consumed == nbuf); break; } } return 0; }
int mc_msg_decompress(mc_msg_t **msgP) { FBI_ASSERT(msgP && *msgP); mc_msg_t *msg = *msgP; if (msg->value.str == NULL || msg->value.len == 0) { return 0; } if (msg->flags & MC_MSG_FLAG_NZLIB_COMPRESSED) { return mc_msg_nzlib_decompress(msgP); } // Not compressed return 0; }
um_status_t um_consume_no_copy(const uint8_t* header, size_t nheader, const uint8_t* body, size_t nbody, uint64_t* reqid_out, mc_msg_t* msg_out) { FBI_ASSERT(header && body && reqid_out && msg_out); um_message_info_t info; um_status_t header_rv = um_parse_header(header, nheader, &info); if (header_rv != um_ok) { return header_rv; } if (!(nheader == info.header_size && nbody == info.body_size && nheader + nbody == info.message_size)) { return um_invalid_range; } entry_list_t elist; entry_list_init(&elist); /* We know that header/body won't be modified here, but the interface is messed up */ ssize_t rv = entry_list_read_from_buf(&elist, (char*)header, nheader, (char*)body, nbody, /*free_buf_when_done=*/0); if (rv < 0 || rv != info.message_size) { return um_message_parse_error; } _parse_info_t parse_info; if (_fill_base_msg(&elist, msg_out, &parse_info) != 0) { return um_message_parse_error; } /* TODO: support stats with no_copy API */ if (parse_info.stats_count > 0) { return um_message_parse_error; } if (_fill_msg_strs(msg_out, &elist, body, &parse_info) != 0) { return um_message_parse_error; } *reqid_out = parse_info.reqid; return um_ok; }
int um_emit_iovs_extended(um_backing_msg_t* bmsg, uint64_t reqid, mc_msg_t* msg, struct iovec* value_iovs, size_t n_value_iovs, emit_iov_cb* emit_iov, void* context) { FBI_ASSERT(bmsg && !bmsg->inuse && msg && msg->op != mc_op_end && emit_iov); _backing_msg_fill(bmsg, reqid, msg, value_iovs, n_value_iovs); if (entry_list_emit_iovs(&bmsg->elist, emit_iov, context)) { um_backing_msg_cleanup(bmsg); return -1; } return 0; }
ssize_t um_write_iovs_extended(um_backing_msg_t* bmsg, uint64_t reqid, mc_msg_t* msg, struct iovec* value_iovs, size_t n_value_iovs, struct iovec* iovs, size_t n_iovs) { FBI_ASSERT(bmsg && !bmsg->inuse && msg && msg->op != mc_op_end && iovs); _backing_msg_fill(bmsg, reqid, msg, value_iovs, n_value_iovs); int iovs_used = entry_list_to_iovecs(&bmsg->elist, iovs, n_iovs); if (iovs_used <= 0) { um_backing_msg_cleanup(bmsg); return -1; } return iovs_used; }
void ProxyThread::stopAndJoin() { if (thread_handle && proxy->router->pid == getpid()) { FBI_ASSERT(proxy->request_queue != nullptr); asox_queue_entry_t entry; entry.type = request_type_router_shutdown; entry.priority = 0; entry.data = nullptr; entry.nbytes = 0; asox_queue_enqueue(proxy->request_queue, &entry); { std::unique_lock<std::mutex> lk(mux); isSafeToDeleteProxy = true; } cv.notify_all(); pthread_join(thread_handle, nullptr); } if (thread_stack) { free(thread_stack); } }
um_status_t um_parse_header(const uint8_t* buf, size_t nbuf, um_message_info_t* info_out) { FBI_ASSERT(info_out); if (nbuf < sizeof(entry_list_msg_t)) { return um_not_enough_data; } entry_list_msg_t* header = (entry_list_msg_t*) buf; if (header->msg_header.magic_byte != ENTRY_LIST_MAGIC_BYTE) { return um_not_umbrella_message; } info_out->message_size = ntoh32(header->total_size); uint16_t nentries = ntoh16(header->nentries); info_out->header_size = sizeof(entry_list_msg_t) + sizeof(um_elist_entry_t) * nentries; if (info_out->header_size > info_out->message_size) { return um_header_parse_error; } info_out->body_size = info_out->message_size - info_out->header_size; return um_ok; }
int um_parser_init(um_parser_t* um_parser) { FBI_ASSERT(um_parser); entry_list_preparer_init(&um_parser->prep); return 0; }
size_t mc_ascii_response_write_iovs(mc_ascii_response_buf_t* buf, const mc_msg_t* req, const mc_msg_t* reply, struct iovec* iovs, size_t max_niovs) { size_t niovs = 0; buf->offset = 0; if (mc_res_is_err(reply->result)) { if (reply->value.len > 0) { if (reply->result == mc_res_client_error) { IOV_WRITE_CONST_STR("CLIENT_ERROR "); } else { IOV_WRITE_CONST_STR("SERVER_ERROR "); } if (reply->err_code != 0) { IOV_FORMAT(buf, "%" PRIu32 " ", reply->err_code); } IOV_WRITE_NSTRING(reply->value); IOV_WRITE_CONST_STR("\r\n"); } else { IOV_WRITE_STR(mc_res_to_response_string(reply->result)); } return niovs; } switch (req->op) { case mc_op_incr: case mc_op_decr: switch (reply->result) { case mc_res_stored: IOV_FORMAT(buf, "%" PRIu64 "\r\n", reply->delta); break; case mc_res_notfound: IOV_WRITE_CONST_STR("NOT_FOUND\r\n"); break; default: goto UNEXPECTED; } break; case mc_op_set: case mc_op_lease_set: case mc_op_add: case mc_op_replace: case mc_op_append: case mc_op_prepend: case mc_op_cas: switch (reply->result) { case mc_res_ok: IOV_WRITE_STR(mc_res_to_response_string(mc_res_stored)); break; case mc_res_stored: case mc_res_stalestored: case mc_res_found: case mc_res_notstored: case mc_res_notfound: case mc_res_exists: IOV_WRITE_STR(mc_res_to_response_string(reply->result)); break; default: goto UNEXPECTED; } break; case mc_op_delete: switch (reply->result) { case mc_res_deleted: case mc_res_notfound: IOV_WRITE_STR(mc_res_to_response_string(reply->result)); break; default: goto UNEXPECTED; } break; case mc_op_get: case mc_op_lease_get: case mc_op_gets: switch (reply->result) { case mc_res_found: IOV_WRITE_CONST_STR("VALUE "); IOV_WRITE_NSTRING(req->key); IOV_FORMAT(buf, " %" PRIu64 " %lu", reply->flags, reply->value.len); if (req->op == mc_op_gets) { IOV_FORMAT(buf, " %" PRIu64, reply->cas); } IOV_WRITE_CONST_STR("\r\n"); IOV_WRITE_NSTRING(reply->value); IOV_WRITE_CONST_STR("\r\n"); break; case mc_res_notfound: if (req->op != mc_op_lease_get) { // misses should have been suppressed! goto UNEXPECTED; } // but lease-get always has a response IOV_WRITE_CONST_STR("LVALUE "); IOV_WRITE_NSTRING(req->key); IOV_FORMAT(buf, " %" PRIu64 " %"PRIu64 " %zu\r\n", reply->lease_id, reply->flags, reply->value.len); IOV_WRITE_NSTRING(reply->value); IOV_WRITE_CONST_STR("\r\n"); break; default: goto UNEXPECTED; } break; case mc_op_metaget: switch (reply->result) { case mc_res_found: /* (META key age: (unknown|\d+); exptime: \d+; from: (\d+\.\d+\.\d+\.\d+|unknown); is_transient: (1|0)\r\n) */ IOV_WRITE_CONST_STR("META "); IOV_WRITE_NSTRING(req->key); IOV_WRITE_CONST_STR(" age: "); if (reply->number == (uint32_t) -1) { IOV_WRITE_CONST_STR("unknown"); } else { IOV_FORMAT(buf, "%d", reply->number); } IOV_WRITE_CONST_STR("; exptime: "); IOV_FORMAT(buf, "%d", reply->exptime); IOV_WRITE_CONST_STR("; from: "); if (reply->ipv == 0) { IOV_WRITE_CONST_STR("unknown"); } else { IOV_WRITE_IP(buf, reply->ipv, &(reply->ip_addr)); } IOV_WRITE_CONST_STR("; is_transient: "); IOV_FORMAT(buf, "%" PRIu64, reply->flags); IOV_WRITE_CONST_STR("\r\n"); break; case mc_res_notfound: break; default: goto UNEXPECTED; } break; case mc_op_end: if (reply->result == mc_res_found) { IOV_WRITE_CONST_STR("END\r\n"); } else { IOV_WRITE_STR(mc_res_to_response_string(reply->result)); } break; case mc_op_stats: switch (reply->result) { case mc_res_ok: { size_t length = 0; char* stats; if (reply->stats) { /* TODO(agartrell) assert(!reply->value.str) * * The assert here can't be turned on until * mcrouter/stats.c:560 has been fixed to not set both * value and stats on the libmc reply */ stats = stats_reply_to_string(reply->stats, reply->number, &length); buf->stats = stats; } else { stats = reply->value.str; length = reply->value.len; } if (!stats) { return 0; } IOV_WRITE(stats, length); break; } default: goto UNEXPECTED; } break; case mc_op_flushall: case mc_op_flushre: IOV_WRITE_CONST_STR("OK\r\n"); break; case mc_op_version: IOV_WRITE_CONST_STR("VERSION "); IOV_WRITE_NSTRING(reply->value); IOV_WRITE_CONST_STR("\r\n"); break; case mc_op_shutdown: if (reply->result == mc_res_ok) { IOV_WRITE_CONST_STR("OK\r\n"); } else { goto UNEXPECTED; } break; case mc_op_exec: switch (reply->result) { case mc_res_ok: IOV_WRITE_NSTRING(reply->value); IOV_WRITE_CONST_STR("\r\n"); break; default: goto UNEXPECTED; } break; default: IOV_WRITE_CONST_STR("SERVER_ERROR unhandled token "); IOV_WRITE_STR(mc_op_to_string(req->op)); IOV_FORMAT(buf, " (%d)\r\n", (int)req->op); break; } return niovs; UNEXPECTED: FBI_ASSERT(niovs == 0); IOV_WRITE_CONST_STR("SERVER_ERROR unexpected result "); IOV_WRITE_STR(mc_res_to_string(reply->result)); IOV_FORMAT(buf, " (%d) for ", (int)reply->result); IOV_WRITE_STR(mc_op_to_string(req->op)); IOV_FORMAT(buf, " (%d)\r\n", (int)req->op); return niovs; }
static int _fill_base_msg(entry_list_t *elist, mc_msg_t* base, _parse_info_t* parse_info) { FBI_ASSERT(elist && base && parse_info); uint64_t i; int numDoubles = 0; parse_info->reqid = 0; parse_info->key_idx = -1; parse_info->value_idx = -1; #ifndef LIBMC_FBTRACE_DISABLE parse_info->fbtrace_idx = -1; #endif parse_info->stats_count = 0; uint64_t field_mask = 0; for (i = 0; i < elist->nentries; i++) { uint16_t tag = ntoh16(elist->entries[i].tag); uint64_t val = ntoh64(elist->entries[i].data.val); // No dup fields (except for stats and double) if ((tag & field_mask) && (tag != msg_stats && tag != msg_double)) { dbg_error("Duplicate field: field_mask = 0x%lX, field = 0x%X\n", field_mask, tag); return -1; } field_mask |= tag; switch (tag) { case msg_op: if (val >= UM_NOPS) { return -1; } base->op = umbrella_op_to_mc[val]; if (base->op == mc_nops) { return -1; } break; case msg_result: if (val >= mc_nres) { return -1; } base->result = umbrella_res_to_mc[val]; break; case msg_reqid: if (val == 0) { return -1; } parse_info->reqid = val; break; case msg_err_code: base->err_code = val; break; case msg_flags: base->flags = val; break; case msg_exptime: base->exptime = val; break; case msg_number: base->number = val; break; case msg_delta: base->delta = val; break; case msg_lease_id: base->lease_id = val; break; case msg_cas: base->cas = val; break; case msg_double: if (numDoubles == 0 ) { memcpy(&base->lowval, &val, sizeof(double)); } else if (numDoubles == 1) { memcpy(&base->highval, &val, sizeof(double)); } else { return -1; } numDoubles++; break; case msg_key: if (parse_info->key_idx == -1) { parse_info->key_idx = i; } break; case msg_value: if (parse_info->value_idx != -1) { return -1; } parse_info->value_idx = i; break; case msg_stats: parse_info->stats_count++; break; #ifndef LIBMC_FBTRACE_DISABLE case msg_fbtrace: if (parse_info->fbtrace_idx != -1) { return -1; } parse_info->fbtrace_idx = i; break; #endif default: return -1; } } if (parse_info->reqid == 0) { return -1; } if (parse_info->key_idx >= (int64_t)elist->nentries) { return -1; } if (parse_info->value_idx >= (int64_t)elist->nentries) { return -1; } if (!valid_msg(base->op, field_mask)) { dbg_error("Invalid message"); return -1; } return 0; }