1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */2/*3 * memcached - memory caching daemon4 *5 * https://www.memcached.org/6 *7 * Copyright 2003 Danga Interactive, Inc. All rights reserved.8 *9 * Use and distribution licensed under the BSD license. See10 * the LICENSE file for full text.11 *12 * Authors:13 * Anatoly Vorobey <mellon@pobox.com>14 * Brad Fitzpatrick <brad@danga.com>15 */16#include "memcached.h"17#include "storage.h"18#include "authfile.h"19#include "restart.h"20#include "slabs_mover.h"21#include <sys/stat.h>22#include <sys/socket.h>23#include <sys/un.h>24#include <signal.h>25#include <sys/param.h>26#include <sys/resource.h>27#include <sys/uio.h>28#include <ctype.h>29#include <stdarg.h>3031/* some POSIX systems need the following definition32 * to get mlockall flags out of sys/mman.h. */33#ifndef _P1003_1B_VISIBLE34#define _P1003_1B_VISIBLE35#endif36#include <pwd.h>37#include <sys/mman.h>38#include <fcntl.h>39#include <netinet/tcp.h>40#include <arpa/inet.h>41#include <errno.h>42#include <stdlib.h>43#include <stdio.h>44#include <string.h>45#include <time.h>46#include <assert.h>47#include <sysexits.h>48#include <stddef.h>4950#ifdef HAVE_GETOPT_LONG51#include <getopt.h>52#endif5354#include "tls.h"5556#include "proto_text.h"57#include "proto_bin.h"58#include "proto_proxy.h"5960#if defined(__FreeBSD__)61#include <sys/sysctl.h>62#endif6364/*65 * forward declarations66 */67static void drive_machine(conn *c);68static int new_socket(struct addrinfo *ai);69static ssize_t tcp_read(conn *arg, void *buf, size_t count);70static ssize_t tcp_sendmsg(conn *arg, struct msghdr *msg, int flags);71static ssize_t tcp_write(conn *arg, void *buf, size_t count);7273enum try_read_result {74 READ_DATA_RECEIVED,75 READ_NO_DATA_RECEIVED,76 READ_ERROR, /** an error occurred (on the socket) (or client closed connection) */77 READ_MEMORY_ERROR /** failed to allocate more memory */78};7980static int try_read_command_negotiate(conn *c);81static int try_read_command_udp(conn *c);8283static enum try_read_result try_read_network(conn *c);84static enum try_read_result try_read_udp(conn *c);8586static int start_conn_timeout_thread(void);8788/* stats */89static void stats_init(void);90static void conn_to_str(const conn *c, char *addr, char *svr_addr);9192/* defaults */93static void settings_init(void);9495/* event handling, network IO */96static void event_handler(const evutil_socket_t fd, const short which, void *arg);97static void conn_close(conn *c);98static void conn_init(void);99static bool update_event(conn *c, const int new_flags);100static void complete_nread(conn *c);101102static void conn_free(conn *c);103104/** exported globals **/105struct stats stats;106struct stats_state stats_state;107struct settings settings;108time_t process_started; /* when the process was started */109conn **conns;110111#ifdef EXTSTORE112/* hoping this is temporary; I'd prefer to cut globals, but will complete this113 * battle another day.114 */115void *ext_storage = NULL;116#endif117/** file scope variables **/118static conn *listen_conn = NULL;119static int max_fds;120static struct event_base *main_base;121122enum transmit_result {123 TRANSMIT_COMPLETE, /** All done writing. */124 TRANSMIT_INCOMPLETE, /** More data remaining to write. */125 TRANSMIT_SOFT_ERROR, /** Can't write any more right now. */126 TRANSMIT_HARD_ERROR /** Can't write (c->state is set to conn_closing) */127};128129/* Default methods to read from/ write to a socket */130ssize_t tcp_read(conn *c, void *buf, size_t count) {131 assert (c != NULL);132 return read(c->sfd, buf, count);133}134135ssize_t tcp_sendmsg(conn *c, struct msghdr *msg, int flags) {136 assert (c != NULL);137 return sendmsg(c->sfd, msg, flags);138}139140ssize_t tcp_write(conn *c, void *buf, size_t count) {141 assert (c != NULL);142 return write(c->sfd, buf, count);143}144145static enum transmit_result transmit(conn *c);146147/* This reduces the latency without adding lots of extra wiring to be able to148 * notify the listener thread of when to listen again.149 * Also, the clock timer could be broken out into its own thread and we150 * can block the listener via a condition.151 */152static volatile bool allow_new_conns = true;153static int stop_main_loop = NOT_STOP;154static struct event maxconnsevent;155static void maxconns_handler(const evutil_socket_t fd, const short which, void *arg) {156 struct timeval t = {.tv_sec = 0, .tv_usec = 10000};157158 if (fd == -42 || allow_new_conns == false) {159 /* reschedule in 10ms if we need to keep polling */160 evtimer_set(&maxconnsevent, maxconns_handler, 0);161 event_base_set(main_base, &maxconnsevent);162 evtimer_add(&maxconnsevent, &t);163 } else {164 evtimer_del(&maxconnsevent);165 accept_new_conns(true);166 }167}168169/*170 * given time value that's either unix time or delta from current unix time, return171 * unix time. Use the fact that delta can't exceed one month (and real time value can't172 * be that low).173 */174rel_time_t realtime(const time_t exptime) {175 /* no. of seconds in 30 days - largest possible delta exptime */176177 if (exptime == 0) return 0; /* 0 means never expire */178179 if (exptime > REALTIME_MAXDELTA) {180 /* if item expiration is at/before the server started, give it an181 expiration time of 1 second after the server started.182 (because 0 means don't expire). without this, we'd183 underflow and wrap around to some large value way in the184 future, effectively making items expiring in the past185 really expiring never */186 if (exptime <= process_started)187 return (rel_time_t)1;188 return (rel_time_t)(exptime - process_started);189 } else {190 return (rel_time_t)(exptime + current_time);191 }192}193194static void stats_init(void) {195 memset(&stats, 0, sizeof(struct stats));196 memset(&stats_state, 0, sizeof(struct stats_state));197 stats_state.accepting_conns = true; /* assuming we start in this state. */198199 /* make the time we started always be 2 seconds before we really200 did, so time(0) - time.started is never zero. if so, things201 like 'settings.oldest_live' which act as booleans as well as202 values are now false in boolean context... */203 process_started = time(0) - ITEM_UPDATE_INTERVAL - 2;204 stats_prefix_init(settings.prefix_delimiter);205}206207void stats_reset(void) {208 STATS_LOCK();209 memset(&stats, 0, sizeof(struct stats));210 stats_prefix_clear();211 STATS_UNLOCK();212 threadlocal_stats_reset();213 item_stats_reset();214}215216static void settings_init(void) {217 settings.use_cas = true;218 settings.access = 0700;219 settings.port = 11211;220 settings.udpport = 0;221 ssl_init_settings();222 /* By default this string should be NULL for getaddrinfo() */223 settings.inter = NULL;224 settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */225 settings.maxconns = 1024; /* to limit connections-related memory to about 5MB */226 settings.verbose = 0;227 settings.oldest_live = 0;228 settings.evict_to_free = 1; /* push old items out of cache when memory runs out */229 settings.socketpath = NULL; /* by default, not using a unix socket */230 settings.auth_file = NULL; /* by default, not using ASCII authentication tokens */231 settings.factor = 1.25;232 settings.chunk_size = 48; /* space for a modest key and value */233 settings.num_threads = 4; /* N workers */234 settings.num_threads_per_udp = 0;235 settings.prefix_delimiter = ':';236 settings.detail_enabled = 0;237 settings.reqs_per_event = 20;238 settings.backlog = 1024;239 settings.binding_protocol = negotiating_prot;240 settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */241 settings.slab_page_size = 1024 * 1024; /* chunks are split from 1MB pages. */242 settings.slab_chunk_size_max = settings.slab_page_size / 2;243 settings.sasl = false;244 settings.maxconns_fast = true;245 settings.lru_crawler = false;246 settings.lru_crawler_sleep = 100;247 settings.lru_crawler_tocrawl = 0;248 settings.lru_maintainer_thread = false;249 settings.lru_segmented = true;250 settings.hot_lru_pct = 20;251 settings.warm_lru_pct = 40;252 settings.hot_max_factor = 0.2;253 settings.warm_max_factor = 2.0;254 settings.temp_lru = false;255 settings.temporary_ttl = 61;256 settings.idle_timeout = 0; /* disabled */257 settings.hashpower_init = 0;258 settings.slab_reassign = true;259 settings.slab_automove = 1;260 settings.slab_automove_version = 0;261 settings.slab_automove_ratio = 0.8;262 settings.slab_automove_window = 10;263 settings.shutdown_command = false;264 settings.tail_repair_time = TAIL_REPAIR_TIME_DEFAULT;265 settings.flush_enabled = true;266 settings.dump_enabled = true;267 settings.crawls_persleep = 1000;268 settings.logger_watcher_buf_size = LOGGER_WATCHER_BUF_SIZE;269 settings.logger_buf_size = LOGGER_BUF_SIZE;270 settings.drop_privileges = false;271 settings.watch_enabled = true;272 settings.read_buf_mem_limit = 0;273#ifdef MEMCACHED_DEBUG274 settings.relaxed_privileges = false;275#endif276 settings.num_napi_ids = 0;277 settings.memory_file = NULL;278#ifdef SOCK_COOKIE_ID279 settings.sock_cookie_id = 0;280#endif281}282283extern pthread_mutex_t conn_lock;284285/* Connection timeout thread bits */286static pthread_t conn_timeout_tid;287static int do_run_conn_timeout_thread;288static pthread_cond_t conn_timeout_cond = PTHREAD_COND_INITIALIZER;289static pthread_mutex_t conn_timeout_lock = PTHREAD_MUTEX_INITIALIZER;290291#define CONNS_PER_SLICE 100292static void *conn_timeout_thread(void *arg) {293 int i;294 conn *c;295 rel_time_t oldest_last_cmd;296 int sleep_time;297 int sleep_slice = max_fds / CONNS_PER_SLICE;298 if (sleep_slice == 0)299 sleep_slice = CONNS_PER_SLICE;300301 useconds_t timeslice = 1000000 / sleep_slice;302303 mutex_lock(&conn_timeout_lock);304 while(do_run_conn_timeout_thread) {305 if (settings.verbose > 2)306 fprintf(stderr, "idle timeout thread at top of connection list\n");307308 oldest_last_cmd = current_time;309310 for (i = 0; i < max_fds; i++) {311 if ((i % CONNS_PER_SLICE) == 0) {312 if (settings.verbose > 2)313 fprintf(stderr, "idle timeout thread sleeping for %ulus\n",314 (unsigned int)timeslice);315 usleep(timeslice);316 }317318 if (!conns[i])319 continue;320321 c = conns[i];322323 if (!IS_TCP(c->transport))324 continue;325326 if (c->state != conn_new_cmd && c->state != conn_read)327 continue;328329 if ((current_time - c->last_cmd_time) > settings.idle_timeout) {330 timeout_conn(c);331 } else {332 if (c->last_cmd_time < oldest_last_cmd)333 oldest_last_cmd = c->last_cmd_time;334 }335 }336337 /* This is the soonest we could have another connection time out */338 sleep_time = settings.idle_timeout - (current_time - oldest_last_cmd) + 1;339 if (sleep_time <= 0)340 sleep_time = 1;341342 if (settings.verbose > 2)343 fprintf(stderr,344 "idle timeout thread finished pass, sleeping for %ds\n",345 sleep_time);346347 struct timeval now;348 struct timespec to_sleep;349 gettimeofday(&now, NULL);350 to_sleep.tv_sec = now.tv_sec + sleep_time;351 to_sleep.tv_nsec = 0;352353 pthread_cond_timedwait(&conn_timeout_cond, &conn_timeout_lock, &to_sleep);354 }355356 mutex_unlock(&conn_timeout_lock);357 return NULL;358}359360static int start_conn_timeout_thread(void) {361 int ret;362363 if (settings.idle_timeout == 0)364 return -1;365366 do_run_conn_timeout_thread = 1;367 if ((ret = pthread_create(&conn_timeout_tid, NULL,368 conn_timeout_thread, NULL)) != 0) {369 fprintf(stderr, "Can't create idle connection timeout thread: %s\n",370 strerror(ret));371 return -1;372 }373 thread_setname(conn_timeout_tid, "mc-idletimeout");374375 return 0;376}377378int stop_conn_timeout_thread(void) {379 if (!do_run_conn_timeout_thread)380 return -1;381 mutex_lock(&conn_timeout_lock);382 do_run_conn_timeout_thread = 0;383 pthread_cond_signal(&conn_timeout_cond);384 mutex_unlock(&conn_timeout_lock);385 pthread_join(conn_timeout_tid, NULL);386 return 0;387}388389/*390 * read buffer cache helper functions391 */392static void rbuf_release(conn *c) {393 if (c->rbuf != NULL && c->rbytes == 0 && !IS_UDP(c->transport)) {394 if (c->rbuf_malloced) {395 free(c->rbuf);396 c->rbuf_malloced = false;397 } else {398 do_cache_free(c->thread->rbuf_cache, c->rbuf);399 }400 c->rsize = 0;401 c->rbuf = NULL;402 c->rcurr = NULL;403 }404}405406static bool rbuf_alloc(conn *c) {407 if (c->rbuf == NULL) {408 c->rbuf = do_cache_alloc(c->thread->rbuf_cache);409 if (!c->rbuf) {410 THR_STATS_LOCK(c->thread);411 c->thread->stats.read_buf_oom++;412 THR_STATS_UNLOCK(c->thread);413 return false;414 }415 c->rsize = READ_BUFFER_SIZE;416 c->rcurr = c->rbuf;417 }418 return true;419}420421// Just for handling huge ASCII multigets.422// The previous system was essentially the same; realloc'ing until big enough,423// then realloc'ing back down after the request finished.424bool rbuf_switch_to_malloc(conn *c) {425 // Might as well start with x2 and work from there.426 size_t size = c->rsize * 2;427 char *tmp = malloc(size);428 if (!tmp)429 return false;430431 memcpy(tmp, c->rcurr, c->rbytes);432 do_cache_free(c->thread->rbuf_cache, c->rbuf);433434 c->rcurr = c->rbuf = tmp;435 c->rsize = size;436 c->rbuf_malloced = true;437 return true;438}439440/*441 * Initializes the connections array. We don't actually allocate connection442 * structures until they're needed, so as to avoid wasting memory when the443 * maximum connection count is much higher than the actual number of444 * connections.445 *446 * This does end up wasting a few pointers' worth of memory for FDs that are447 * used for things other than connections, but that's worth it in exchange for448 * being able to directly index the conns array by FD.449 */450static void conn_init(void) {451 /* We're unlikely to see an FD much higher than maxconns. */452 int next_fd = dup(1);453 if (next_fd < 0) {454 perror("Failed to duplicate file descriptor\n");455 exit(1);456 }457 int headroom = 10; /* account for extra unexpected open FDs */458 struct rlimit rl;459460 max_fds = settings.maxconns + headroom + next_fd;461462 /* But if possible, get the actual highest FD we can possibly ever see. */463 if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {464 max_fds = rl.rlim_max;465 } else {466 fprintf(stderr, "Failed to query maximum file descriptor; "467 "falling back to maxconns\n");468 }469470 close(next_fd);471472 if ((conns = calloc(max_fds, sizeof(conn *))) == NULL) {473 fprintf(stderr, "Failed to allocate connection structures\n");474 /* This is unrecoverable so bail out early. */475 exit(1);476 }477}478479static const char *prot_text(enum protocol prot) {480 char *rv = "unknown";481 switch(prot) {482 case ascii_prot:483 rv = "ascii";484 break;485 case binary_prot:486 rv = "binary";487 break;488 case negotiating_prot:489 rv = "auto-negotiate";490 break;491#ifdef PROXY492 case proxy_prot:493 rv = "proxy";494 break;495#endif496 }497 return rv;498}499500void conn_close_idle(conn *c) {501 if (settings.idle_timeout > 0 &&502 (current_time - c->last_cmd_time) > settings.idle_timeout) {503 if (c->state != conn_new_cmd && c->state != conn_read) {504 if (settings.verbose > 1)505 fprintf(stderr,506 "fd %d wants to timeout, but isn't in read state", c->sfd);507 return;508 }509510 if (settings.verbose > 1)511 fprintf(stderr, "Closing idle fd %d\n", c->sfd);512513 pthread_mutex_lock(&c->thread->stats.mutex);514 c->thread->stats.idle_kicks++;515 pthread_mutex_unlock(&c->thread->stats.mutex);516517 c->close_reason = IDLE_TIMEOUT_CLOSE;518519 conn_set_state(c, conn_closing);520 drive_machine(c);521 }522}523524static void _conn_event_readd(conn *c) {525 c->ev_flags = EV_READ | EV_PERSIST;526 event_set(&c->event, c->sfd, c->ev_flags, event_handler, (void *)c);527 event_base_set(c->thread->base, &c->event);528529 // TODO: call conn_cleanup/fail/etc530 if (event_add(&c->event, 0) == -1) {531 perror("event_add");532 }533}534535/* bring conn back from a sidethread. could have had its event base moved. */536void conn_worker_readd(conn *c) {537 assert(c->resps_suspended == 0); // TODO: remove assert.538539 switch (c->state) {540 case conn_closing:541 drive_machine(c);542 break;543 case conn_io_pending:544 // The event listener was removed as more data showed up while545 // waiting for the async response.546 _conn_event_readd(c);547 // Explicit fall-through.548 case conn_io_queue:549 conn_set_state(c, conn_io_resume);550 // schedule the event, which just runs drive_machine outside of551 // any recursion here.552 event_active(&c->event, 0, 0);553 break;554 case conn_nread:555 // ran IO queue while waiting for set payload.556 case conn_write:557 case conn_mwrite:558 case conn_read:559 case conn_parse_cmd:560 // No-ops if we weren't in a suspended state to begin with561 // TODO: which other states for this?562 break;563 default:564 event_del(&c->event);565 _conn_event_readd(c);566 conn_set_state(c, conn_new_cmd);567 }568569}570571void thread_io_queue_add(LIBEVENT_THREAD *t, int type, void *ctx, io_queue_stack_cb cb) {572 io_queue_t *q = t->io_queues;573 while (q->type != IO_QUEUE_NONE) {574 q++;575 }576 q->type = type;577 q->ctx = ctx;578 q->submit_cb = cb;579 STAILQ_INIT(&q->stack);580 return;581}582583io_queue_t *thread_io_queue_get(LIBEVENT_THREAD *t, int type) {584 io_queue_t *q = t->io_queues;585 while (q->type != IO_QUEUE_NONE) {586 if (q->type == type) {587 return q;588 }589 q++;590 }591 return NULL;592}593594void thread_io_queue_submit(LIBEVENT_THREAD *t) {595 t->conns_tosubmit = 0;596 for (io_queue_t *q = t->io_queues; q->type != IO_QUEUE_NONE; q++) {597 // submission callback must consume the queue598 if (!STAILQ_EMPTY(&q->stack)) {599 q->submit_cb(q);600 assert(STAILQ_EMPTY(&q->stack));601 }602 }603}604605// called to return a single IO object to the original worker thread.606void conn_io_queue_return(io_pending_t *io) {607 io->return_cb(io);608}609610conn *conn_new(const int sfd, enum conn_states init_state,611 const int event_flags,612 const int read_buffer_size, enum network_transport transport,613 struct event_base *base, void *ssl, uint64_t conntag,614 enum protocol bproto) {615 conn *c;616617 assert(sfd >= 0 && sfd < max_fds);618 c = conns[sfd];619620 if (NULL == c) {621 if (!(c = (conn *)calloc(1, sizeof(conn)))) {622 STATS_LOCK();623 stats.malloc_fails++;624 STATS_UNLOCK();625 fprintf(stderr, "Failed to allocate connection object\n");626 return NULL;627 }628 MEMCACHED_CONN_CREATE(c);629 c->read = NULL;630 c->sendmsg = NULL;631 c->write = NULL;632 c->rbuf = NULL;633634 c->rsize = read_buffer_size;635636 // UDP connections use a persistent static buffer.637 if (c->rsize) {638 c->rbuf = (char *)malloc((size_t)c->rsize);639 }640641 if (c->rsize && c->rbuf == NULL) {642 conn_free(c);643 STATS_LOCK();644 stats.malloc_fails++;645 STATS_UNLOCK();646 fprintf(stderr, "Failed to allocate buffers for connection\n");647 return NULL;648 }649650651 STATS_LOCK();652 stats_state.conn_structs++;653 STATS_UNLOCK();654655 c->sfd = sfd;656 conns[sfd] = c;657 }658659 c->transport = transport;660 c->protocol = bproto;661 c->tag = conntag;662663 /* unix socket mode doesn't need this, so zeroed out. but why664 * is this done for every command? presumably for UDP665 * mode. */666 if (!settings.socketpath) {667 c->request_addr_size = sizeof(c->request_addr);668 } else {669 c->request_addr_size = 0;670 }671672 if (transport == tcp_transport && init_state == conn_new_cmd) {673 if (getpeername(sfd, (struct sockaddr *) &c->request_addr,674 &c->request_addr_size)) {675 perror("getpeername");676 memset(&c->request_addr, 0, sizeof(c->request_addr));677 }678 }679680 if (init_state == conn_new_cmd) {681 LOGGER_LOG(NULL, LOG_CONNEVENTS, LOGGER_CONNECTION_NEW, NULL,682 &c->request_addr, c->request_addr_size, c->transport, 0, sfd);683 }684685 if (settings.verbose > 1) {686 if (init_state == conn_listening) {687 fprintf(stderr, "<%d server listening (%s)\n", sfd,688 prot_text(c->protocol));689 } else if (IS_UDP(transport)) {690 fprintf(stderr, "<%d server listening (udp)\n", sfd);691 } else if (c->protocol == negotiating_prot) {692 fprintf(stderr, "<%d new auto-negotiating client connection\n",693 sfd);694 } else if (c->protocol == ascii_prot) {695 fprintf(stderr, "<%d new ascii client connection.\n", sfd);696 } else if (c->protocol == binary_prot) {697 fprintf(stderr, "<%d new binary client connection.\n", sfd);698#ifdef PROXY699 } else if (c->protocol == proxy_prot) {700 fprintf(stderr, "<%d new proxy client connection.\n", sfd);701#endif702 } else {703 fprintf(stderr, "<%d new unknown (%d) client connection\n",704 sfd, c->protocol);705 assert(false);706 }707 }708709 c->state = init_state;710 c->rlbytes = 0;711 c->cmd = -1;712 c->rbytes = 0;713 c->rcurr = c->rbuf;714 c->ritem = 0;715 c->rbuf_malloced = false;716 c->item_malloced = false;717 c->sasl_started = false;718 c->close_after_write = false;719 c->last_cmd_time = current_time; /* initialize for idle kicker */720 assert(c->resps_suspended == 0);721722 c->item = 0;723 c->ssl = NULL;724#ifdef TLS725 c->ssl_wbuf = NULL;726#endif727728 if (ssl) {729 // musn't get here without ssl enabled.730 assert(settings.ssl_enabled);731 ssl_init_conn(c, ssl);732 c->ssl_enabled = true;733 } else {734 c->read = tcp_read;735 c->sendmsg = tcp_sendmsg;736 c->write = tcp_write;737 c->ssl_enabled = false;738 }739740 if (IS_UDP(transport)) {741 c->try_read_command = try_read_command_udp;742 } else {743 switch (c->protocol) {744 case ascii_prot:745 if (settings.auth_file == NULL) {746 c->authenticated = true;747 c->try_read_command = try_read_command_ascii;748 } else {749 c->authenticated = false;750 c->try_read_command = try_read_command_asciiauth;751 }752 break;753 case binary_prot:754 // binprot handles its own authentication via SASL parsing.755 c->authenticated = false;756 c->try_read_command = try_read_command_binary;757 break;758 case negotiating_prot:759 c->try_read_command = try_read_command_negotiate;760 break;761#ifdef PROXY762 case proxy_prot:763 c->try_read_command = try_read_command_proxy;764 break;765#endif766 }767 }768769 event_set(&c->event, sfd, event_flags, event_handler, (void *)c);770 event_base_set(base, &c->event);771 c->ev_flags = event_flags;772773 if (event_add(&c->event, 0) == -1) {774 perror("event_add");775 return NULL;776 }777778 STATS_LOCK();779 stats_state.curr_conns++;780 stats.total_conns++;781 STATS_UNLOCK();782783 MEMCACHED_CONN_ALLOCATE(c->sfd);784785 return c;786}787788void conn_release_items(conn *c) {789 assert(c != NULL);790791 if (c->item) {792 if (c->item_malloced) {793 free(c->item);794 c->item_malloced = false;795 } else {796 item_remove(c->item);797 }798 c->item = 0;799 }800801 // Cull any unsent responses.802 if (c->resp_head) {803 mc_resp *resp = c->resp_head;804 // r_f() handles the chain maintenance.805 while (resp) {806 // temporary by default. hide behind a debug flag in the future:807 // double free detection. Transmit loops can drop out early, but808 // here we could infinite loop.809 if (resp->free) {810 fprintf(stderr, "ERROR: double free detected during conn_release_items(): [%d] [%s]\n",811 c->sfd, c->protocol == binary_prot ? "binary" : "ascii");812 // Since this is a critical failure, just leak the memory.813 // If these errors are seen, an abort() can be used instead.814 c->resp_head = NULL;815 c->resp = NULL;816 break;817 }818 resp = resp_finish(c, resp);819 }820 }821}822823static void conn_cleanup(conn *c) {824 assert(c != NULL);825826 conn_release_items(c);827#ifdef PROXY828 if (c->proxy_rctx) {829 proxy_cleanup_conn(c);830 }831#endif832 if (c->sasl_conn) {833 assert(settings.sasl);834 sasl_dispose(&c->sasl_conn);835 c->sasl_conn = NULL;836 }837838 if (IS_UDP(c->transport)) {839 conn_set_state(c, conn_read);840 }841}842843/*844 * Frees a connection.845 */846void conn_free(conn *c) {847 if (c) {848 assert(c != NULL);849 assert(c->sfd >= 0 && c->sfd < max_fds);850851 MEMCACHED_CONN_DESTROY(c);852 conns[c->sfd] = NULL;853 if (c->rbuf)854 free(c->rbuf);855#ifdef TLS856 if (c->ssl_wbuf)857 c->ssl_wbuf = NULL;858#endif859860 free(c);861 }862}863864static void conn_close(conn *c) {865 assert(c != NULL);866867 if (c->thread) {868 LOGGER_LOG(c->thread->l, LOG_CONNEVENTS, LOGGER_CONNECTION_CLOSE, NULL,869 &c->request_addr, c->request_addr_size, c->transport,870 c->close_reason, c->sfd);871 }872873 /* delete the event, the socket and the conn */874 event_del(&c->event);875876 if (settings.verbose > 1)877 fprintf(stderr, "<%d connection closed.\n", c->sfd);878879 conn_cleanup(c);880881 // force release of read buffer.882 if (c->thread) {883 c->rbytes = 0;884 rbuf_release(c);885 }886887 MEMCACHED_CONN_RELEASE(c->sfd);888 conn_set_state(c, conn_closed);889 if (c->ssl_enabled) {890 ssl_conn_close(c->ssl);891 }892 close(c->sfd);893 c->close_reason = 0;894 pthread_mutex_lock(&conn_lock);895 allow_new_conns = true;896 pthread_mutex_unlock(&conn_lock);897898 STATS_LOCK();899 stats_state.curr_conns--;900 STATS_UNLOCK();901902 return;903}904905// Since some connections might be off on side threads and some are managed as906// listeners we need to walk through them all from a central point.907// Must be called with all worker threads hung or in the process of closing.908void conn_close_all(void) {909 int i;910 for (i = 0; i < max_fds; i++) {911 if (conns[i] && conns[i]->state != conn_closed) {912 conn_close(conns[i]);913 }914 }915}916917/**918 * Convert a state name to a human readable form.919 */920static const char *state_text(enum conn_states state) {921 const char* const statenames[] = { "conn_listening",922 "conn_new_cmd",923 "conn_waiting",924 "conn_read",925 "conn_parse_cmd",926 "conn_write",927 "conn_nread",928 "conn_swallow",929 "conn_closing",930 "conn_mwrite",931 "conn_closed",932 "conn_watch",933 "conn_io_queue",934 "conn_io_resume",935 "conn_io_pending" };936 return statenames[state];937}938939/*940 * Sets a connection's current state in the state machine. Any special941 * processing that needs to happen on certain state transitions can942 * happen here.943 */944void conn_set_state(conn *c, enum conn_states state) {945 assert(c != NULL);946 assert(state >= conn_listening && state < conn_max_state);947948 if (state != c->state) {949 if (settings.verbose > 2) {950 fprintf(stderr, "%d: going from %s to %s\n",951 c->sfd, state_text(c->state),952 state_text(state));953 }954955 if (state == conn_write || state == conn_mwrite) {956 MEMCACHED_PROCESS_COMMAND_END(c->sfd, c->resp->wbuf, c->resp->wbytes);957 }958 c->state = state;959 }960}961962/*963 * response object helper functions964 */965void resp_reset(mc_resp *resp) {966 if (resp->item) {967 item_remove(resp->item);968 resp->item = NULL;969 }970 if (resp->write_and_free) {971#ifdef PROXY972 if (resp->proxy_res) {973 LIBEVENT_THREAD *t = resp->bundle->thread;974 t->proxy_buffer_memory_used -= resp->wbytes;975 }976#endif977 free(resp->write_and_free);978 resp->write_and_free = NULL;979 }980 resp->wbytes = 0;981 resp->tosend = 0;982 resp->iovcnt = 0;983 resp->chunked_data_iov = 0;984 resp->chunked_total = 0;985 resp->skip = false;986}987988void resp_add_iov(mc_resp *resp, const void *buf, int len) {989 assert(resp->iovcnt < MC_RESP_IOVCOUNT);990 int x = resp->iovcnt;991 resp->iov[x].iov_base = (void *)buf;992 resp->iov[x].iov_len = len;993 resp->iovcnt++;994 resp->tosend += len;995}996997// Notes that an IOV should be handled as a chunked item header.998// TODO: I'm hoping this isn't a permanent abstraction while I learn what the999// API should be.1000void resp_add_chunked_iov(mc_resp *resp, const void *buf, int len) {1001 resp->chunked_data_iov = resp->iovcnt;1002 resp->chunked_total = len;1003 resp_add_iov(resp, buf, len);1004}10051006// resp_allocate and resp_free are a wrapper around read buffers which makes1007// read buffers the only network memory to track.1008// Normally this would be too excessive. In this case it allows end users to1009// track a single memory limit for ephemeral connection buffers.1010// Fancy bit twiddling tricks are avoided to help keep this straightforward.1011static mc_resp* resp_allocate(conn *c) {1012 LIBEVENT_THREAD *th = c->thread;1013 mc_resp *resp = NULL;1014 mc_resp_bundle *b = th->open_bundle;10151016 if (b != NULL) {1017 for (int i = 0; i < MAX_RESP_PER_BUNDLE; i++) {1018 // loop around starting from the most likely to be free1019 int x = (i + b->next_check) % MAX_RESP_PER_BUNDLE;1020 if (b->r[x].free) {1021 resp = &b->r[x];1022 b->next_check = x+1;1023 break;1024 }1025 }10261027 if (resp != NULL) {1028 b->refcount++;1029 memset(resp, 0, sizeof(*resp));1030 resp->free = false; // redundant, for clarity.1031 resp->bundle = b;1032 if (b->refcount == MAX_RESP_PER_BUNDLE) {1033 assert(b->prev == NULL);1034 // We only allocate off the head. Assign new head.1035 th->open_bundle = b->next;1036 // Remove ourselves from the list.1037 if (b->next) {1038 b->next->prev = 0;1039 b->next = 0;1040 }1041 }1042 }1043 }10441045 if (resp == NULL) {1046 assert(th->open_bundle == NULL);1047 b = do_cache_alloc(th->rbuf_cache);1048 if (b) {1049 THR_STATS_LOCK(th);1050 th->stats.response_obj_bytes += READ_BUFFER_SIZE;1051 THR_STATS_UNLOCK(th);1052 b->next_check = 1;1053 b->refcount = 1;1054 for (int i = 0; i < MAX_RESP_PER_BUNDLE; i++) {1055 b->r[i].free = true;1056 }1057 b->next = 0;1058 b->prev = 0;1059 b->thread = th;1060 th->open_bundle = b;1061 resp = &b->r[0];1062 memset(resp, 0, sizeof(*resp));1063 resp->free = false; // redundant. for clarity.1064 resp->bundle = b;1065 } else {1066 return NULL;1067 }1068 }10691070 return resp;1071}10721073void resp_free(LIBEVENT_THREAD *th, mc_resp *resp) {1074 mc_resp_bundle *b = resp->bundle;10751076 resp->free = true;1077 b->refcount--;1078 if (b->refcount == 0) {1079 if (b == th->open_bundle && b->next == 0) {1080 // This is the final bundle. Just hold and reuse to skip init loop1081 assert(b->prev == 0);1082 b->next_check = 0;1083 } else {1084 // Assert that we're either in the list or at the head.1085 assert((b->next || b->prev) || b == th->open_bundle);10861087 // unlink from list.1088 mc_resp_bundle **head = &th->open_bundle;1089 if (*head == b) *head = b->next;1090 // Not tracking the tail.1091 assert(b->next != b && b->prev != b);10921093 if (b->next) b->next->prev = b->prev;1094 if (b->prev) b->prev->next = b->next;10951096 // Now completely done with this buffer.1097 do_cache_free(th->rbuf_cache, b);1098 THR_STATS_LOCK(th);1099 th->stats.response_obj_bytes -= READ_BUFFER_SIZE;1100 THR_STATS_UNLOCK(th);1101 }1102 } else {1103 mc_resp_bundle **head = &th->open_bundle;1104 // NOTE: since we're not tracking tail, latest free ends up in head.1105 if (b == th->open_bundle || (b->prev || b->next)) {1106 // If we're already linked, leave it in place to save CPU.1107 } else {1108 // Non-zero refcount, need to link into the freelist.1109 b->prev = 0;1110 b->next = *head;1111 if (b->next) b->next->prev = b;1112 *head = b;1113 }11141115 }1116 THR_STATS_LOCK(th);1117 th->stats.response_obj_count--;1118 THR_STATS_UNLOCK(th);1119}11201121bool resp_start(conn *c) {1122 mc_resp *resp = resp_allocate(c);1123 if (!resp) {1124 THR_STATS_LOCK(c->thread);1125 c->thread->stats.response_obj_oom++;1126 THR_STATS_UNLOCK(c->thread);1127 return false;1128 }11291130 // handling the stats counters here to simplify testing1131 THR_STATS_LOCK(c->thread);1132 c->thread->stats.response_obj_count++;1133 THR_STATS_UNLOCK(c->thread);11341135 if (!c->resp_head) {1136 c->resp_head = resp;1137 }1138 if (!c->resp) {1139 c->resp = resp;1140 } else {1141 c->resp->next = resp;1142 c->resp = resp;1143 }1144 if (IS_UDP(c->transport)) {1145 // need to hold on to some data for async responses.1146 c->resp->request_id = c->request_id;1147 c->resp->request_addr = c->request_addr;1148 c->resp->request_addr_size = c->request_addr_size;1149 }1150 return true;1151}11521153mc_resp *resp_start_unlinked(conn *c) {1154 mc_resp *resp = resp_allocate(c);1155 if (!resp) {1156 THR_STATS_LOCK(c->thread);1157 c->thread->stats.response_obj_oom++;1158 THR_STATS_UNLOCK(c->thread);1159 return false;1160 }11611162 // handling the stats counters here to simplify testing1163 THR_STATS_LOCK(c->thread);1164 c->thread->stats.response_obj_count++;1165 THR_STATS_UNLOCK(c->thread);11661167 if (IS_UDP(c->transport)) {1168 // need to hold on to some data for async responses.1169 c->resp->request_id = c->request_id;1170 c->resp->request_addr = c->request_addr;1171 c->resp->request_addr_size = c->request_addr_size;1172 }11731174 return resp;1175}11761177// returns next response in chain.1178mc_resp* resp_finish(conn *c, mc_resp *resp) {1179 mc_resp *next = resp->next;1180 if (resp->item) {1181 // TODO: cache hash value in resp obj?1182 item_remove(resp->item);1183 resp->item = NULL;1184 }1185 if (resp->write_and_free) {1186#ifdef PROXY1187 if (resp->proxy_res) {1188 LIBEVENT_THREAD *t = resp->bundle->thread;1189 t->proxy_buffer_memory_used -= resp->wbytes;1190 }1191#endif1192 free(resp->write_and_free);1193 }1194 if (resp->io_pending) {1195 io_pending_t *io = resp->io_pending;1196 // If we had a pending IO, tell it to internally clean up then return1197 // the main object back to our thread cache.1198 io->finalize_cb(io);1199 do_cache_free(c->thread->io_cache, io);1200 resp->io_pending = NULL;1201 }1202 if (c->resp_head == resp) {1203 c->resp_head = next;1204 }1205 if (c->resp == resp) {1206 c->resp = NULL;1207 }1208 resp_free(c->thread, resp);1209 return next;1210}12111212// tells if connection has a depth of response objects to process.1213bool resp_has_stack(conn *c) {1214 return c->resp_head->next != NULL ? true : false;1215}12161217void out_string(conn *c, const char *str) {1218 size_t len;1219 assert(c != NULL);1220 mc_resp *resp = c->resp;12211222 // if response was original filled with something, but we're now writing1223 // out an error or similar, have to reset the object first.1224 // TODO: since this is often redundant with allocation, how many callers1225 // are actually requiring it be reset? Can we fast test by just looking at1226 // tosend and reset if nonzero?1227 resp_reset(resp);12281229 if (resp->noreply) {1230 // TODO: just invalidate the response since nothing's been attempted1231 // to send yet?1232 resp->skip = true;1233 if (settings.verbose > 1)1234 fprintf(stderr, ">%d NOREPLY %s\n", c->sfd, str);1235 conn_set_state(c, conn_new_cmd);1236 return;1237 }12381239 if (settings.verbose > 1)1240 fprintf(stderr, ">%d %s\n", c->sfd, str);12411242 // Fill response object with static string.12431244 len = strlen(str);1245 if ((len + 2) > WRITE_BUFFER_SIZE) {1246 /* ought to be always enough. just fail for simplicity */1247 str = "SERVER_ERROR output line too long";1248 len = strlen(str);1249 }12501251 memcpy(resp->wbuf, str, len);1252 memcpy(resp->wbuf + len, "\r\n", 2);1253 resp_add_iov(resp, resp->wbuf, len + 2);12541255 conn_set_state(c, conn_new_cmd);1256 return;1257}12581259// For metaget-style ASCII commands. Ignores noreply, ensuring clients see1260// protocol level errors.1261void out_errstring(conn *c, const char *str) {1262 c->resp->noreply = false;1263 out_string(c, str);1264}12651266/*1267 * Outputs a protocol-specific "out of memory" error. For ASCII clients,1268 * this is equivalent to out_string().1269 */1270void out_of_memory(conn *c, char *ascii_error) {1271 const static char error_prefix[] = "SERVER_ERROR ";1272 const static int error_prefix_len = sizeof(error_prefix) - 1;12731274 if (c->protocol == binary_prot) {1275 /* Strip off the generic error prefix; it's irrelevant in binary */1276 if (!strncmp(ascii_error, error_prefix, error_prefix_len)) {1277 ascii_error += error_prefix_len;1278 }1279 write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, ascii_error, 0);1280 } else {1281 out_string(c, ascii_error);1282 }1283}12841285static void append_bin_stats(const char *key, const uint16_t klen,1286 const char *val, const uint32_t vlen,1287 conn *c) {1288 char *buf = c->stats.buffer + c->stats.offset;1289 uint32_t bodylen = klen + vlen;1290 protocol_binary_response_header header = {1291 .response.magic = (uint8_t)PROTOCOL_BINARY_RES,1292 .response.opcode = PROTOCOL_BINARY_CMD_STAT,1293 .response.keylen = (uint16_t)htons(klen),1294 .response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES,1295 .response.bodylen = htonl(bodylen),1296 .response.opaque = c->opaque1297 };12981299 memcpy(buf, header.bytes, sizeof(header.response));1300 buf += sizeof(header.response);13011302 if (klen > 0) {1303 memcpy(buf, key, klen);1304 buf += klen;13051306 if (vlen > 0) {1307 memcpy(buf, val, vlen);1308 }1309 }13101311 c->stats.offset += sizeof(header.response) + bodylen;1312}13131314static void append_ascii_stats(const char *key, const uint16_t klen,1315 const char *val, const uint32_t vlen,1316 conn *c) {1317 char *pos = c->stats.buffer + c->stats.offset;1318 uint32_t nbytes = 0;1319 int remaining = c->stats.size - c->stats.offset;1320 int room = remaining - 1;13211322 if (klen == 0 && vlen == 0) {1323 nbytes = snprintf(pos, room, "END\r\n");1324 } else if (vlen == 0) {1325 nbytes = snprintf(pos, room, "STAT %s\r\n", key);1326 } else {1327 nbytes = snprintf(pos, room, "STAT %s %s\r\n", key, val);1328 }13291330 c->stats.offset += nbytes;1331}13321333static bool grow_stats_buf(conn *c, size_t needed) {1334 size_t nsize = c->stats.size;1335 size_t available = nsize - c->stats.offset;1336 bool rv = true;13371338 /* Special case: No buffer -- need to allocate fresh */1339 if (c->stats.buffer == NULL) {1340 nsize = 1024;1341 available = c->stats.size = c->stats.offset = 0;1342 }13431344 while (needed > available) {1345 assert(nsize > 0);1346 nsize = nsize << 1;1347 available = nsize - c->stats.offset;1348 }13491350 if (nsize != c->stats.size) {1351 char *ptr = realloc(c->stats.buffer, nsize);1352 if (ptr) {1353 c->stats.buffer = ptr;1354 c->stats.size = nsize;1355 } else {1356 STATS_LOCK();1357 stats.malloc_fails++;1358 STATS_UNLOCK();1359 rv = false;1360 }1361 }13621363 return rv;1364}13651366void append_stats(const char *key, const uint16_t klen,1367 const char *val, const uint32_t vlen,1368 const void *cookie)1369{1370 /* value without a key is invalid */1371 if (klen == 0 && vlen > 0) {1372 return;1373 }13741375 conn *c = (conn*)cookie;13761377 if (c->protocol == binary_prot) {1378 size_t needed = vlen + klen + sizeof(protocol_binary_response_header);1379 if (!grow_stats_buf(c, needed)) {1380 return;1381 }1382 append_bin_stats(key, klen, val, vlen, c);1383 } else {1384 size_t needed = vlen + klen + 10; // 10 == "STAT = \r\n"1385 if (!grow_stats_buf(c, needed)) {1386 return;1387 }1388 append_ascii_stats(key, klen, val, vlen, c);1389 }13901391 assert(c->stats.offset <= c->stats.size);1392}13931394static void reset_cmd_handler(conn *c) {1395 c->cmd = -1;1396 c->substate = bin_no_state;1397 if (c->item != NULL) {1398 // TODO: Any other way to get here?1399 // SASL auth was mistakenly using it. Nothing else should?1400 if (c->item_malloced) {1401 free(c->item);1402 c->item_malloced = false;1403 } else {1404 item_remove(c->item);1405 }1406 c->item = NULL;1407 }1408 if (c->rbytes > 0) {1409 conn_set_state(c, conn_parse_cmd);1410 } else if (c->resp_head) {1411 conn_set_state(c, conn_mwrite);1412 } else if (c->ssl_enabled && ssl_pending(c->ssl)) {1413 // We may have pending bytes in the TLS BIO because of a mismatch1414 // between TLS records and occasional direct reads from the network.1415 // Round-trip back through the read code instead of stopping.1416 conn_set_state(c, conn_read);1417 } else {1418 conn_set_state(c, conn_waiting);1419 }1420}14211422static void complete_nread(conn *c) {1423 assert(c != NULL);1424#ifdef PROXY1425 assert(c->protocol == ascii_prot1426 || c->protocol == binary_prot1427 || c->protocol == proxy_prot);1428#else1429 assert(c->protocol == ascii_prot1430 || c->protocol == binary_prot);1431#endif1432 if (c->protocol == ascii_prot) {1433 complete_nread_ascii(c);1434 } else if (c->protocol == binary_prot) {1435 complete_nread_binary(c);1436#ifdef PROXY1437 } else if (c->protocol == proxy_prot) {1438 complete_nread_proxy(c);1439#endif1440 }1441}14421443/* Destination must always be chunked */1444/* This should be part of item.c */1445static int _store_item_copy_chunks(item *d_it, item *s_it, const int len) {1446 item_chunk *dch = (item_chunk *) ITEM_schunk(d_it);1447 /* Advance dch until we find free space */1448 while (dch->size == dch->used) {1449 if (dch->next) {1450 dch = dch->next;1451 } else {1452 break;1453 }1454 }14551456 if (s_it->it_flags & ITEM_CHUNKED) {1457 int remain = len;1458 item_chunk *sch = (item_chunk *) ITEM_schunk(s_it);1459 int copied = 0;1460 /* Fills dch's to capacity, not straight copy sch in case data is1461 * being added or removed (ie append/prepend)1462 */1463 while (sch && dch && remain) {1464 assert(dch->used <= dch->size);1465 int todo = (dch->size - dch->used < sch->used - copied)1466 ? dch->size - dch->used : sch->used - copied;1467 if (remain < todo)1468 todo = remain;1469 memcpy(dch->data + dch->used, sch->data + copied, todo);1470 dch->used += todo;1471 copied += todo;1472 remain -= todo;1473 assert(dch->used <= dch->size);1474 if (dch->size == dch->used) {1475 item_chunk *tch = do_item_alloc_chunk(dch, remain);1476 if (tch) {1477 dch = tch;1478 } else {1479 return -1;1480 }1481 }1482 assert(copied <= sch->used);1483 if (copied == sch->used) {1484 copied = 0;1485 sch = sch->next;1486 }1487 }1488 /* assert that the destination had enough space for the source */1489 assert(remain == 0);1490 } else {1491 int done = 0;1492 /* Fill dch's via a non-chunked item. */1493 while (len > done && dch) {1494 int todo = (dch->size - dch->used < len - done)1495 ? dch->size - dch->used : len - done;1496 //assert(dch->size - dch->used != 0);1497 memcpy(dch->data + dch->used, ITEM_data(s_it) + done, todo);1498 done += todo;1499 dch->used += todo;1500 assert(dch->used <= dch->size);1501 if (dch->size == dch->used) {1502 item_chunk *tch = do_item_alloc_chunk(dch, len - done);1503 if (tch) {1504 dch = tch;1505 } else {1506 return -1;1507 }1508 }1509 }1510 assert(len == done);1511 }1512 return 0;1513}15141515static int _store_item_copy_data(int comm, item *old_it, item *new_it, item *add_it) {1516 if (comm == NREAD_APPEND || comm == NREAD_APPENDVIV) {1517 if (new_it->it_flags & ITEM_CHUNKED) {1518 if (_store_item_copy_chunks(new_it, old_it, old_it->nbytes - 2) == -1 ||1519 _store_item_copy_chunks(new_it, add_it, add_it->nbytes) == -1) {1520 return -1;1521 }1522 } else {1523 memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);1524 memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(add_it), add_it->nbytes);1525 }1526 } else {1527 /* NREAD_PREPEND */1528 if (new_it->it_flags & ITEM_CHUNKED) {1529 if (_store_item_copy_chunks(new_it, add_it, add_it->nbytes - 2) == -1 ||1530 _store_item_copy_chunks(new_it, old_it, old_it->nbytes) == -1) {1531 return -1;1532 }1533 } else {1534 memcpy(ITEM_data(new_it), ITEM_data(add_it), add_it->nbytes);1535 memcpy(ITEM_data(new_it) + add_it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);1536 }1537 }1538 return 0;1539}15401541/*1542 * Stores an item in the cache according to the semantics of one of the set1543 * commands. Protected by the item lock.1544 *1545 * Returns the state of storage.1546 */1547enum store_item_type do_store_item(item *it, int comm, LIBEVENT_THREAD *t, const uint32_t hv, int *nbytes, uint64_t *cas, uint64_t cas_in, bool cas_stale) {1548 char *key = ITEM_key(it);1549 item *old_it = do_item_get(key, it->nkey, hv, t, DONT_UPDATE);1550 enum store_item_type stored = NOT_STORED;15511552 enum cas_result { CAS_NONE, CAS_MATCH, CAS_BADVAL, CAS_STALE, CAS_MISS };15531554 item *new_it = NULL;1555 client_flags_t flags;15561557 /* Do the CAS test up front so we can apply to all store modes */1558 enum cas_result cas_res = CAS_NONE;15591560 bool do_store = false;1561 if (old_it != NULL) {1562 // Most of the CAS work requires something to compare to.1563 uint64_t it_cas = ITEM_get_cas(it);1564 uint64_t old_cas = ITEM_get_cas(old_it);1565 if (it_cas == 0) {1566 cas_res = CAS_NONE;1567 } else if (it_cas == old_cas) {1568 cas_res = CAS_MATCH;1569 } else if (cas_stale && it_cas < old_cas) {1570 cas_res = CAS_STALE;1571 } else {1572 cas_res = CAS_BADVAL;1573 }15741575 switch (comm) {1576 case NREAD_ADD:1577 /* add only adds a nonexistent item, but promote to head of LRU */1578 do_item_update(old_it);1579 break;1580 case NREAD_CAS:1581 if (cas_res == CAS_MATCH) {1582 // cas validates1583 // it and old_it may belong to different classes.1584 // I'm updating the stats for the one that's getting pushed out1585 pthread_mutex_lock(&t->stats.mutex);1586 t->stats.slab_stats[ITEM_clsid(old_it)].cas_hits++;1587 pthread_mutex_unlock(&t->stats.mutex);1588 do_store = true;1589 } else if (cas_res == CAS_STALE) {1590 // if we're allowed to set a stale value, CAS must be lower than1591 // the current item's CAS.1592 // This replaces the value, but should preserve TTL, and stale1593 // item marker bit + token sent if exists.1594 it->exptime = old_it->exptime;1595 it->it_flags |= ITEM_STALE;1596 if (old_it->it_flags & ITEM_TOKEN_SENT) {1597 it->it_flags |= ITEM_TOKEN_SENT;1598 }15991600 pthread_mutex_lock(&t->stats.mutex);1601 t->stats.slab_stats[ITEM_clsid(old_it)].cas_hits++;1602 pthread_mutex_unlock(&t->stats.mutex);1603 do_store = true;1604 } else {1605 // NONE or BADVAL are the same for CAS cmd1606 pthread_mutex_lock(&t->stats.mutex);1607 t->stats.slab_stats[ITEM_clsid(old_it)].cas_badval++;1608 pthread_mutex_unlock(&t->stats.mutex);16091610 if (settings.verbose > 1) {1611 fprintf(stderr, "CAS: failure: expected %llu, got %llu\n",1612 (unsigned long long)ITEM_get_cas(old_it),1613 (unsigned long long)ITEM_get_cas(it));1614 }1615 stored = EXISTS;1616 }1617 break;1618 case NREAD_APPEND:1619 case NREAD_PREPEND:1620 case NREAD_APPENDVIV:1621 case NREAD_PREPENDVIV:1622 if (cas_res != CAS_NONE && cas_res != CAS_MATCH) {1623 stored = EXISTS;1624 break;1625 }1626#ifdef EXTSTORE1627 if ((old_it->it_flags & ITEM_HDR) != 0) {1628 /* block append/prepend from working with extstore-d items.1629 * leave response code to NOT_STORED default */1630 break;1631 }1632#endif1633 /* we have it and old_it here - alloc memory to hold both */1634 FLAGS_CONV(old_it, flags);1635 new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */);16361637 // OOM trying to copy.1638 if (new_it == NULL)1639 break;1640 /* copy data from it and old_it to new_it */1641 if (_store_item_copy_data(comm, old_it, new_it, it) == -1) {1642 // failed data copy1643 break;1644 } else {1645 // refcount of new_it is 1 here. will end up 2 after link.1646 // it's original ref is managed outside of this function1647 it = new_it;1648 do_store = true;1649 // Upstream final object size for meta1650 if (nbytes != NULL) {1651 *nbytes = it->nbytes;1652 }1653 }1654 break;1655 case NREAD_REPLACE:1656 case NREAD_SET:1657 do_store = true;1658 break;1659 }16601661 if (do_store) {1662 STORAGE_delete(t->storage, old_it);1663 item_replace(old_it, it, hv, cas_in);1664 stored = STORED;1665 }16661667 do_item_remove(old_it); /* release our reference */1668 if (new_it != NULL) {1669 // append/prepend end up with an extra reference for new_it.1670 do_item_remove(new_it);1671 }1672 } else {1673 /* No pre-existing item to replace or compare to. */1674 if (ITEM_get_cas(it) != 0) {1675 /* Asked for a CAS match but nothing to compare it to. */1676 cas_res = CAS_MISS;1677 }16781679 switch (comm) {1680 case NREAD_ADD:1681 case NREAD_SET:1682 case NREAD_APPENDVIV:1683 case NREAD_PREPENDVIV:1684 do_store = true;1685 break;1686 case NREAD_CAS:1687 // LRU expired1688 stored = NOT_FOUND;1689 pthread_mutex_lock(&t->stats.mutex);1690 t->stats.cas_misses++;1691 pthread_mutex_unlock(&t->stats.mutex);1692 break;1693 case NREAD_REPLACE:1694 case NREAD_APPEND:1695 case NREAD_PREPEND:1696 /* Requires an existing item. */1697 break;1698 }16991700 if (do_store) {1701 do_item_link(it, hv, cas_in);1702 stored = STORED;1703 }1704 }17051706 if (stored == STORED && cas != NULL) {1707 *cas = ITEM_get_cas(it);1708 }1709 LOGGER_LOG(t->l, LOG_MUTATIONS, LOGGER_ITEM_STORE, NULL,1710 stored, comm, ITEM_key(it), it->nkey, it->nbytes, it->exptime,1711 ITEM_clsid(it), t->cur_sfd);17121713 return stored;1714}17151716/* set up a connection to write a buffer then free it, used for stats */1717void write_and_free(conn *c, char *buf, int bytes) {1718 if (buf) {1719 mc_resp *resp = c->resp;1720 resp->write_and_free = buf;1721 resp_add_iov(resp, buf, bytes);1722 conn_set_state(c, conn_new_cmd);1723 } else {1724 out_of_memory(c, "SERVER_ERROR out of memory writing stats");1725 }1726}17271728void append_stat(const char *name, ADD_STAT add_stats, conn *c,1729 const char *fmt, ...) {1730 char val_str[STAT_VAL_LEN];1731 int vlen;1732 va_list ap;17331734 assert(name);1735 assert(add_stats);1736 assert(c);1737 assert(fmt);17381739 va_start(ap, fmt);1740 vlen = vsnprintf(val_str, sizeof(val_str) - 1, fmt, ap);1741 va_end(ap);17421743 add_stats(name, strlen(name), val_str, vlen, c);1744}17451746/* return server specific stats only */1747void server_stats(ADD_STAT add_stats, void *c) {1748 pid_t pid = getpid();1749 rel_time_t now = current_time;17501751 struct thread_stats thread_stats;1752 threadlocal_stats_aggregate(&thread_stats);1753 struct slab_stats slab_stats;1754 slab_stats_aggregate(&thread_stats, &slab_stats);1755#ifndef WIN321756 struct rusage usage;1757 getrusage(RUSAGE_SELF, &usage);1758#endif /* !WIN32 */17591760 STATS_LOCK();17611762 APPEND_STAT("pid", "%lu", (long)pid);1763 APPEND_STAT("uptime", "%u", now - ITEM_UPDATE_INTERVAL);1764 APPEND_STAT("time", "%ld", now + (long)process_started);1765 APPEND_STAT("version", "%s", VERSION);1766 APPEND_STAT("libevent", "%s", event_get_version());1767 APPEND_STAT("pointer_size", "%d", (int)(8 * sizeof(void *)));17681769#ifndef WIN321770 append_stat("rusage_user", add_stats, c, "%ld.%06ld",1771 (long)usage.ru_utime.tv_sec,1772 (long)usage.ru_utime.tv_usec);1773 append_stat("rusage_system", add_stats, c, "%ld.%06ld",1774 (long)usage.ru_stime.tv_sec,1775 (long)usage.ru_stime.tv_usec);1776#endif /* !WIN32 */17771778 APPEND_STAT("max_connections", "%d", settings.maxconns);1779 APPEND_STAT("curr_connections", "%llu", (unsigned long long)stats_state.curr_conns - 1);1780 APPEND_STAT("total_connections", "%llu", (unsigned long long)stats.total_conns);1781 if (settings.maxconns_fast) {1782 APPEND_STAT("rejected_connections", "%llu", (unsigned long long)stats.rejected_conns);1783 }1784 APPEND_STAT("connection_structures", "%u", stats_state.conn_structs);1785 APPEND_STAT("response_obj_oom", "%llu", (unsigned long long)thread_stats.response_obj_oom);1786 APPEND_STAT("response_obj_count", "%llu", (unsigned long long)thread_stats.response_obj_count);1787 APPEND_STAT("response_obj_bytes", "%llu", (unsigned long long)thread_stats.response_obj_bytes);1788 APPEND_STAT("read_buf_count", "%llu", (unsigned long long)thread_stats.read_buf_count);1789 APPEND_STAT("read_buf_bytes", "%llu", (unsigned long long)thread_stats.read_buf_bytes);1790 APPEND_STAT("read_buf_bytes_free", "%llu", (unsigned long long)thread_stats.read_buf_bytes_free);1791 APPEND_STAT("read_buf_oom", "%llu", (unsigned long long)thread_stats.read_buf_oom);1792 APPEND_STAT("reserved_fds", "%u", stats_state.reserved_fds);1793#ifdef PROXY1794 if (settings.proxy_enabled) {1795 APPEND_STAT("proxy_conn_requests", "%llu", (unsigned long long)thread_stats.proxy_conn_requests);1796 APPEND_STAT("proxy_conn_errors", "%llu", (unsigned long long)thread_stats.proxy_conn_errors);1797 APPEND_STAT("proxy_conn_oom", "%llu", (unsigned long long)thread_stats.proxy_conn_oom);1798 APPEND_STAT("proxy_req_active", "%lld", (long long int)thread_stats.proxy_req_active);1799 }1800#endif1801 APPEND_STAT("cmd_get", "%llu", (unsigned long long)thread_stats.get_cmds);1802 APPEND_STAT("cmd_set", "%llu", (unsigned long long)slab_stats.set_cmds);1803 APPEND_STAT("cmd_flush", "%llu", (unsigned long long)thread_stats.flush_cmds);1804 APPEND_STAT("cmd_touch", "%llu", (unsigned long long)thread_stats.touch_cmds);1805 APPEND_STAT("cmd_meta", "%llu", (unsigned long long)thread_stats.meta_cmds);1806 APPEND_STAT("get_hits", "%llu", (unsigned long long)slab_stats.get_hits);1807 APPEND_STAT("get_misses", "%llu", (unsigned long long)thread_stats.get_misses);1808 APPEND_STAT("get_expired", "%llu", (unsigned long long)thread_stats.get_expired);1809 APPEND_STAT("get_flushed", "%llu", (unsigned long long)thread_stats.get_flushed);1810#ifdef EXTSTORE1811 if (ext_storage) {1812 APPEND_STAT("get_extstore", "%llu", (unsigned long long)thread_stats.get_extstore);1813 APPEND_STAT("get_aborted_extstore", "%llu", (unsigned long long)thread_stats.get_aborted_extstore);1814 APPEND_STAT("get_oom_extstore", "%llu", (unsigned long long)thread_stats.get_oom_extstore);1815 APPEND_STAT("recache_from_extstore", "%llu", (unsigned long long)thread_stats.recache_from_extstore);1816 APPEND_STAT("miss_from_extstore", "%llu", (unsigned long long)thread_stats.miss_from_extstore);1817 APPEND_STAT("badcrc_from_extstore", "%llu", (unsigned long long)thread_stats.badcrc_from_extstore);1818 }1819#endif1820 APPEND_STAT("delete_misses", "%llu", (unsigned long long)thread_stats.delete_misses);1821 APPEND_STAT("delete_hits", "%llu", (unsigned long long)slab_stats.delete_hits);1822 APPEND_STAT("incr_misses", "%llu", (unsigned long long)thread_stats.incr_misses);1823 APPEND_STAT("incr_hits", "%llu", (unsigned long long)slab_stats.incr_hits);1824 APPEND_STAT("decr_misses", "%llu", (unsigned long long)thread_stats.decr_misses);1825 APPEND_STAT("decr_hits", "%llu", (unsigned long long)slab_stats.decr_hits);1826 APPEND_STAT("cas_misses", "%llu", (unsigned long long)thread_stats.cas_misses);1827 APPEND_STAT("cas_hits", "%llu", (unsigned long long)slab_stats.cas_hits);1828 APPEND_STAT("cas_badval", "%llu", (unsigned long long)slab_stats.cas_badval);1829 APPEND_STAT("touch_hits", "%llu", (unsigned long long)slab_stats.touch_hits);1830 APPEND_STAT("touch_misses", "%llu", (unsigned long long)thread_stats.touch_misses);1831 APPEND_STAT("store_too_large", "%llu", (unsigned long long)thread_stats.store_too_large);1832 APPEND_STAT("store_no_memory", "%llu", (unsigned long long)thread_stats.store_no_memory);1833 APPEND_STAT("auth_cmds", "%llu", (unsigned long long)thread_stats.auth_cmds);1834 APPEND_STAT("auth_errors", "%llu", (unsigned long long)thread_stats.auth_errors);1835 if (settings.idle_timeout) {1836 APPEND_STAT("idle_kicks", "%llu", (unsigned long long)thread_stats.idle_kicks);1837 }1838 APPEND_STAT("bytes_read", "%llu", (unsigned long long)thread_stats.bytes_read);1839 APPEND_STAT("bytes_written", "%llu", (unsigned long long)thread_stats.bytes_written);1840 APPEND_STAT("limit_maxbytes", "%llu", (unsigned long long)settings.maxbytes);1841 APPEND_STAT("accepting_conns", "%u", stats_state.accepting_conns);1842 APPEND_STAT("listen_disabled_num", "%llu", (unsigned long long)stats.listen_disabled_num);1843 APPEND_STAT("time_in_listen_disabled_us", "%llu", stats.time_in_listen_disabled_us);1844 APPEND_STAT("threads", "%d", settings.num_threads);1845 APPEND_STAT("conn_yields", "%llu", (unsigned long long)thread_stats.conn_yields);1846 APPEND_STAT("hash_power_level", "%u", stats_state.hash_power_level);1847 APPEND_STAT("hash_bytes", "%llu", (unsigned long long)stats_state.hash_bytes);1848 APPEND_STAT("hash_is_expanding", "%u", stats_state.hash_is_expanding);1849 if (settings.slab_reassign) {1850 const char *busy_status = stats.slab_reassign_last_busy_status;1851 if (!busy_status) {1852 // Ensure we can't be NULL, for portability reasons.1853 busy_status = "none";1854 }1855 APPEND_STAT("slab_reassign_rescues", "%llu", stats.slab_reassign_rescues);1856 APPEND_STAT("slab_reassign_chunk_rescues", "%llu", stats.slab_reassign_chunk_rescues);1857 APPEND_STAT("slab_reassign_inline_reclaim", "%llu", stats.slab_reassign_inline_reclaim);1858 APPEND_STAT("slab_reassign_busy_items", "%llu", stats.slab_reassign_busy_items);1859 APPEND_STAT("slab_reassign_busy_deletes", "%llu", stats.slab_reassign_busy_deletes);1860 APPEND_STAT("slab_reassign_busy_nomem", "%llu", stats.slab_reassign_busy_nomem);1861 APPEND_STAT("slab_reassign_last_busy_status", "%s", busy_status);1862 APPEND_STAT("slab_reassign_running", "%u", stats_state.slab_reassign_running);1863 APPEND_STAT("slabs_moved", "%llu", stats.slabs_moved);1864 }1865 if (settings.lru_crawler) {1866 APPEND_STAT("lru_crawler_running", "%u", stats_state.lru_crawler_running);1867 APPEND_STAT("lru_crawler_starts", "%u", stats.lru_crawler_starts);1868 }1869 if (settings.lru_maintainer_thread) {1870 APPEND_STAT("lru_maintainer_juggles", "%llu", (unsigned long long)stats.lru_maintainer_juggles);1871 }1872 APPEND_STAT("malloc_fails", "%llu",1873 (unsigned long long)stats.malloc_fails);1874 APPEND_STAT("log_worker_dropped", "%llu", (unsigned long long)stats.log_worker_dropped);1875 APPEND_STAT("log_worker_written", "%llu", (unsigned long long)stats.log_worker_written);1876 APPEND_STAT("log_watcher_skipped", "%llu", (unsigned long long)stats.log_watcher_skipped);1877 APPEND_STAT("log_watcher_sent", "%llu", (unsigned long long)stats.log_watcher_sent);1878 APPEND_STAT("log_watchers", "%llu", (unsigned long long)stats_state.log_watchers);1879 STATS_UNLOCK();1880#ifdef EXTSTORE1881 storage_stats(add_stats, c);1882#endif1883#ifdef PROXY1884 proxy_stats(settings.proxy_ctx, add_stats, c);1885#endif1886#ifdef TLS1887 if (settings.ssl_enabled) {1888 if (settings.ssl_session_cache) {1889 APPEND_STAT("ssl_new_sessions", "%llu", (unsigned long long)stats.ssl_new_sessions);1890 }1891 APPEND_STAT("ssl_handshake_errors", "%llu", (unsigned long long)stats.ssl_handshake_errors);1892 APPEND_STAT("ssl_proto_errors", "%llu", (unsigned long long)stats.ssl_proto_errors);1893 APPEND_STAT("time_since_server_cert_refresh", "%u", now - settings.ssl_last_cert_refresh_time);1894 }1895#endif1896 APPEND_STAT("unexpected_napi_ids", "%llu", (unsigned long long)stats.unexpected_napi_ids);1897 APPEND_STAT("round_robin_fallback", "%llu", (unsigned long long)stats.round_robin_fallback);1898}18991900void process_stat_settings(ADD_STAT add_stats, void *c) {1901 assert(add_stats);1902 APPEND_STAT("maxbytes", "%llu", (unsigned long long)settings.maxbytes);1903 APPEND_STAT("maxconns", "%d", settings.maxconns);1904 APPEND_STAT("tcpport", "%d", settings.port);1905 APPEND_STAT("udpport", "%d", settings.udpport);1906 APPEND_STAT("inter", "%s", settings.inter ? settings.inter : "NULL");1907 APPEND_STAT("verbosity", "%d", settings.verbose);1908 APPEND_STAT("oldest", "%lu", (unsigned long)settings.oldest_live);1909 APPEND_STAT("evictions", "%s", settings.evict_to_free ? "on" : "off");1910 APPEND_STAT("domain_socket", "%s",1911 settings.socketpath ? settings.socketpath : "NULL");1912 APPEND_STAT("umask", "%o", settings.access);1913 APPEND_STAT("shutdown_command", "%s",1914 settings.shutdown_command ? "yes" : "no");1915 APPEND_STAT("growth_factor", "%.2f", settings.factor);1916 APPEND_STAT("chunk_size", "%d", settings.chunk_size);1917 APPEND_STAT("num_threads", "%d", settings.num_threads);1918 APPEND_STAT("num_threads_per_udp", "%d", settings.num_threads_per_udp);1919 APPEND_STAT("stat_key_prefix", "%c", settings.prefix_delimiter);1920 APPEND_STAT("detail_enabled", "%s",1921 settings.detail_enabled ? "yes" : "no");1922 APPEND_STAT("reqs_per_event", "%d", settings.reqs_per_event);1923 APPEND_STAT("cas_enabled", "%s", settings.use_cas ? "yes" : "no");1924 APPEND_STAT("tcp_backlog", "%d", settings.backlog);1925 APPEND_STAT("binding_protocol", "%s",1926 prot_text(settings.binding_protocol));1927 APPEND_STAT("auth_enabled_sasl", "%s", settings.sasl ? "yes" : "no");1928 APPEND_STAT("auth_enabled_ascii", "%s", settings.auth_file ? settings.auth_file : "no");1929 APPEND_STAT("item_size_max", "%d", settings.item_size_max);1930 APPEND_STAT("maxconns_fast", "%s", settings.maxconns_fast ? "yes" : "no");1931 APPEND_STAT("hashpower_init", "%d", settings.hashpower_init);1932 APPEND_STAT("slab_reassign", "%s", settings.slab_reassign ? "yes" : "no");1933 APPEND_STAT("slab_automove", "%d", settings.slab_automove);1934 APPEND_STAT("slab_automove_ratio", "%.2f", settings.slab_automove_ratio);1935 APPEND_STAT("slab_automove_window", "%u", settings.slab_automove_window);1936 APPEND_STAT("slab_chunk_max", "%d", settings.slab_chunk_size_max);1937 APPEND_STAT("lru_crawler", "%s", settings.lru_crawler ? "yes" : "no");1938 APPEND_STAT("lru_crawler_sleep", "%d", settings.lru_crawler_sleep);1939 APPEND_STAT("lru_crawler_tocrawl", "%lu", (unsigned long)settings.lru_crawler_tocrawl);1940 APPEND_STAT("tail_repair_time", "%d", settings.tail_repair_time);1941 APPEND_STAT("flush_enabled", "%s", settings.flush_enabled ? "yes" : "no");1942 APPEND_STAT("dump_enabled", "%s", settings.dump_enabled ? "yes" : "no");1943 APPEND_STAT("hash_algorithm", "%s", settings.hash_algorithm);1944 APPEND_STAT("lru_maintainer_thread", "%s", settings.lru_maintainer_thread ? "yes" : "no");1945 APPEND_STAT("lru_segmented", "%s", settings.lru_segmented ? "yes" : "no");1946 APPEND_STAT("hot_lru_pct", "%d", settings.hot_lru_pct);1947 APPEND_STAT("warm_lru_pct", "%d", settings.warm_lru_pct);1948 APPEND_STAT("hot_max_factor", "%.2f", settings.hot_max_factor);1949 APPEND_STAT("warm_max_factor", "%.2f", settings.warm_max_factor);1950 APPEND_STAT("temp_lru", "%s", settings.temp_lru ? "yes" : "no");1951 APPEND_STAT("temporary_ttl", "%u", settings.temporary_ttl);1952 APPEND_STAT("idle_timeout", "%d", settings.idle_timeout);1953 APPEND_STAT("watcher_logbuf_size", "%u", settings.logger_watcher_buf_size);1954 APPEND_STAT("worker_logbuf_size", "%u", settings.logger_buf_size);1955 APPEND_STAT("read_buf_mem_limit", "%u", settings.read_buf_mem_limit);1956 APPEND_STAT("track_sizes", "%s", item_stats_sizes_status() ? "yes" : "no");1957 APPEND_STAT("inline_ascii_response", "%s", "no"); // setting is dead, cannot be yes.1958#ifdef HAVE_DROP_PRIVILEGES1959 APPEND_STAT("drop_privileges", "%s", settings.drop_privileges ? "yes" : "no");1960#endif1961#ifdef EXTSTORE1962 APPEND_STAT("ext_item_size", "%u", settings.ext_item_size);1963 APPEND_STAT("ext_item_age", "%u", settings.ext_item_age);1964 APPEND_STAT("ext_low_ttl", "%u", settings.ext_low_ttl);1965 APPEND_STAT("ext_recache_rate", "%u", settings.ext_recache_rate);1966 APPEND_STAT("ext_wbuf_size", "%u", settings.ext_wbuf_size);1967 APPEND_STAT("ext_compact_under", "%u", settings.ext_compact_under);1968 APPEND_STAT("ext_drop_under", "%u", settings.ext_drop_under);1969 APPEND_STAT("ext_max_sleep", "%u", settings.ext_max_sleep);1970 APPEND_STAT("ext_max_frag", "%.2f", settings.ext_max_frag);1971 APPEND_STAT("slab_automove_freeratio", "%.3f", settings.slab_automove_freeratio);1972 APPEND_STAT("ext_drop_unread", "%s", settings.ext_drop_unread ? "yes" : "no");1973#endif1974#ifdef TLS1975 APPEND_STAT("ssl_enabled", "%s", settings.ssl_enabled ? "yes" : "no");1976 APPEND_STAT("ssl_chain_cert", "%s", settings.ssl_chain_cert);1977 APPEND_STAT("ssl_key", "%s", settings.ssl_key);1978 APPEND_STAT("ssl_verify_mode", "%d", settings.ssl_verify_mode);1979 APPEND_STAT("ssl_keyformat", "%d", settings.ssl_keyformat);1980 APPEND_STAT("ssl_ciphers", "%s", settings.ssl_ciphers ? settings.ssl_ciphers : "NULL");1981 APPEND_STAT("ssl_ca_cert", "%s", settings.ssl_ca_cert ? settings.ssl_ca_cert : "NULL");1982 APPEND_STAT("ssl_wbuf_size", "%u", settings.ssl_wbuf_size);1983 APPEND_STAT("ssl_session_cache", "%s", settings.ssl_session_cache ? "yes" : "no");1984 APPEND_STAT("ssl_kernel_tls", "%s", settings.ssl_kernel_tls ? "yes" : "no");1985 APPEND_STAT("ssl_min_version", "%s", ssl_proto_text(settings.ssl_min_version));1986#endif1987#ifdef PROXY1988 APPEND_STAT("proxy_enabled", "%s", settings.proxy_enabled ? "yes" : "no");1989 APPEND_STAT("proxy_uring_enabled", "%s", settings.proxy_uring ? "yes" : "no");1990#endif1991 APPEND_STAT("num_napi_ids", "%d", settings.num_napi_ids);1992 APPEND_STAT("memory_file", "%s", settings.memory_file);1993 APPEND_STAT("client_flags_size", "%d", sizeof(client_flags_t));1994}19951996static int nz_strcmp(int nzlength, const char *nz, const char *z) {1997 int zlength=strlen(z);1998 return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1;1999}
Findings
✓ No findings reported for this file.