xref: /memcached-1.4.29/memcached.c (revision 690a9a9d)
16d74e134SBrad Fitzpatrick /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2977dfd19SBrad Fitzpatrick /*
3977dfd19SBrad Fitzpatrick  *  memcached - memory caching daemon
4977dfd19SBrad Fitzpatrick  *
5e875bea0Sdormando  *       http://www.memcached.org/
6977dfd19SBrad Fitzpatrick  *
7977dfd19SBrad Fitzpatrick  *  Copyright 2003 Danga Interactive, Inc.  All rights reserved.
8977dfd19SBrad Fitzpatrick  *
90c3b47f4SBrad Fitzpatrick  *  Use and distribution licensed under the BSD license.  See
100c3b47f4SBrad Fitzpatrick  *  the LICENSE file for full text.
11977dfd19SBrad Fitzpatrick  *
12977dfd19SBrad Fitzpatrick  *  Authors:
13977dfd19SBrad Fitzpatrick  *      Anatoly Vorobey <[email protected]>
14977dfd19SBrad Fitzpatrick  *      Brad Fitzpatrick <[email protected]>
15977dfd19SBrad Fitzpatrick  */
1656b8339eSSteven Grimm #include "memcached.h"
1732f382b6SBrad Fitzpatrick #include <sys/stat.h>
1832f382b6SBrad Fitzpatrick #include <sys/socket.h>
19c9607c6dSBrad Fitzpatrick #include <sys/un.h>
20f454731eSTrond Norbye #include <signal.h>
2170c1b5f6SSteven Grimm #include <sys/param.h>
2232f382b6SBrad Fitzpatrick #include <sys/resource.h>
234bf01b99SEric Hodel #include <sys/uio.h>
246aafe58eSDustin Sallings #include <ctype.h>
25dd713869SDustin Sallings #include <stdarg.h>
264bf01b99SEric Hodel 
278405f723SEvan Martin /* some POSIX systems need the following definition
288405f723SEvan Martin  * to get mlockall flags out of sys/mman.h.  */
298405f723SEvan Martin #ifndef _P1003_1B_VISIBLE
308405f723SEvan Martin #define _P1003_1B_VISIBLE
318405f723SEvan Martin #endif
32c9607c6dSBrad Fitzpatrick /* need this to get IOV_MAX on some platforms. */
33c9607c6dSBrad Fitzpatrick #ifndef __need_IOV_MAX
34c9607c6dSBrad Fitzpatrick #define __need_IOV_MAX
35c9607c6dSBrad Fitzpatrick #endif
364961114aSBrad Fitzpatrick #include <pwd.h>
3732f382b6SBrad Fitzpatrick #include <sys/mman.h>
3832f382b6SBrad Fitzpatrick #include <fcntl.h>
39386c274dSAnatoly Vorobey #include <netinet/tcp.h>
401b533267SBrad Fitzpatrick #include <arpa/inet.h>
4132f382b6SBrad Fitzpatrick #include <errno.h>
42c6975ef4SPaul Lindner #include <stdlib.h>
43c6975ef4SPaul Lindner #include <stdio.h>
44c6975ef4SPaul Lindner #include <string.h>
451b533267SBrad Fitzpatrick #include <time.h>
4618a72ad2SBrad Fitzpatrick #include <assert.h>
47c9607c6dSBrad Fitzpatrick #include <limits.h>
487d8344ebSDustin Sallings #include <sysexits.h>
497bb82191STrond Norbye #include <stddef.h>
5086969ea4SBrad Fitzpatrick 
51aad0cd3fSEric Hodel /* FreeBSD 4.x doesn't have IOV_MAX exposed. */
52aad0cd3fSEric Hodel #ifndef IOV_MAX
5361269bbdSGuillaume Delacour #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__GNU__)
54aad0cd3fSEric Hodel # define IOV_MAX 1024
556cdc6d50SGuillaume Delacour /* GNU/Hurd don't set MAXPATHLEN
566cdc6d50SGuillaume Delacour  * http://www.gnu.org/software/hurd/hurd/porting/guidelines.html#PATH_MAX_tt_MAX_PATH_tt_MAXPATHL */
576cdc6d50SGuillaume Delacour #ifndef MAXPATHLEN
586cdc6d50SGuillaume Delacour #define MAXPATHLEN 4096
596cdc6d50SGuillaume Delacour #endif
60aad0cd3fSEric Hodel #endif
61aad0cd3fSEric Hodel #endif
62aad0cd3fSEric Hodel 
6377dde9f9SPaul Lindner /*
6477dde9f9SPaul Lindner  * forward declarations
6577dde9f9SPaul Lindner  */
6677dde9f9SPaul Lindner static void drive_machine(conn *c);
67a61a6900SBrian Aker static int new_socket(struct addrinfo *ai);
6877dde9f9SPaul Lindner static int try_read_command(conn *c);
69496384caSSteve Yen 
70496384caSSteve Yen enum try_read_result {
71496384caSSteve Yen     READ_DATA_RECEIVED,
72496384caSSteve Yen     READ_NO_DATA_RECEIVED,
7381176472Sclark.kang     READ_ERROR,            /** an error occurred (on the socket) (or client closed connection) */
74496384caSSteve Yen     READ_MEMORY_ERROR      /** failed to allocate more memory */
75496384caSSteve Yen };
76496384caSSteve Yen 
77496384caSSteve Yen static enum try_read_result try_read_network(conn *c);
78496384caSSteve Yen static enum try_read_result try_read_udp(conn *c);
79496384caSSteve Yen 
803ed60cddSDustin Sallings static void conn_set_state(conn *c, enum conn_states state);
8183ba6bd9SJay Grizzard static int start_conn_timeout_thread();
8277dde9f9SPaul Lindner 
8377dde9f9SPaul Lindner /* stats */
8477dde9f9SPaul Lindner static void stats_init(void);
8517df5c0eSTrond Norbye static void server_stats(ADD_STAT add_stats, conn *c);
8617df5c0eSTrond Norbye static void process_stat_settings(ADD_STAT add_stats, void *c);
87ae7fb537SSteven Grimm static void conn_to_str(const conn *c, char *buf);
8852778791SDustin Sallings 
8977dde9f9SPaul Lindner 
9077dde9f9SPaul Lindner /* defaults */
9177dde9f9SPaul Lindner static void settings_init(void);
9277dde9f9SPaul Lindner 
9377dde9f9SPaul Lindner /* event handling, network IO */
9477dde9f9SPaul Lindner static void event_handler(const int fd, const short which, void *arg);
9577dde9f9SPaul Lindner static void conn_close(conn *c);
9677dde9f9SPaul Lindner static void conn_init(void);
9777dde9f9SPaul Lindner static bool update_event(conn *c, const int new_flags);
9877dde9f9SPaul Lindner static void complete_nread(conn *c);
9977dde9f9SPaul Lindner static void process_command(conn *c, char *command);
100f4a8e7ffSToru Maesaka static void write_and_free(conn *c, char *buf, int bytes);
10177dde9f9SPaul Lindner static int ensure_iov_space(conn *c);
10277dde9f9SPaul Lindner static int add_iov(conn *c, const void *buf, int len);
103b05653f9Sdormando static int add_chunked_item_iovs(conn *c, item *it, int len);
10477dde9f9SPaul Lindner static int add_msghdr(conn *c);
105d7757c3eSSteven Grimm static void write_bin_error(conn *c, protocol_binary_response_status err,
106e60caafaSSteven Grimm                             const char *errstr, int swallow);
107f1307c4dSDustin Sallings 
10878955139STim Yardley static void conn_free(conn *c);
10977dde9f9SPaul Lindner 
11077dde9f9SPaul Lindner /** exported globals **/
11160d70942SAnatoly Vorobey struct stats stats;
112cb01d504Sdormando struct stats_state stats_state;
11360d70942SAnatoly Vorobey struct settings settings;
1141fdfb7e9STrond Norbye time_t process_started;     /* when the process was started */
11570c1b5f6SSteven Grimm conn **conns;
11686969ea4SBrad Fitzpatrick 
11710698baeSdormando struct slab_rebalance slab_rebal;
11810698baeSdormando volatile int slab_rebalance_signal;
11910698baeSdormando 
12077dde9f9SPaul Lindner /** file scope variables **/
1219150c85bSBrian Aker static conn *listen_conn = NULL;
12270c1b5f6SSteven Grimm static int max_fds;
12356b8339eSSteven Grimm static struct event_base *main_base;
12486969ea4SBrad Fitzpatrick 
125f30083c7SSteve Yen enum transmit_result {
126f30083c7SSteve Yen     TRANSMIT_COMPLETE,   /** All done writing. */
127f30083c7SSteve Yen     TRANSMIT_INCOMPLETE, /** More data remaining to write. */
128f30083c7SSteve Yen     TRANSMIT_SOFT_ERROR, /** Can't write any more right now. */
129f30083c7SSteve Yen     TRANSMIT_HARD_ERROR  /** Can't write (c->state is set to conn_closing) */
130f30083c7SSteve Yen };
131f30083c7SSteve Yen 
132f30083c7SSteve Yen static enum transmit_result transmit(conn *c);
13386969ea4SBrad Fitzpatrick 
134ac939723Sdormando /* This reduces the latency without adding lots of extra wiring to be able to
135ac939723Sdormando  * notify the listener thread of when to listen again.
136ac939723Sdormando  * Also, the clock timer could be broken out into its own thread and we
137ac939723Sdormando  * can block the listener via a condition.
138ac939723Sdormando  */
139ac939723Sdormando static volatile bool allow_new_conns = true;
140ac939723Sdormando static struct event maxconnsevent;
maxconns_handler(const int fd,const short which,void * arg)141ac939723Sdormando static void maxconns_handler(const int fd, const short which, void *arg) {
142ac939723Sdormando     struct timeval t = {.tv_sec = 0, .tv_usec = 10000};
143ac939723Sdormando 
1442c560909Sdormando     if (fd == -42 || allow_new_conns == false) {
145ac939723Sdormando         /* reschedule in 10ms if we need to keep polling */
146ac939723Sdormando         evtimer_set(&maxconnsevent, maxconns_handler, 0);
147ac939723Sdormando         event_base_set(main_base, &maxconnsevent);
148ac939723Sdormando         evtimer_add(&maxconnsevent, &t);
149ac939723Sdormando     } else {
150ac939723Sdormando         evtimer_del(&maxconnsevent);
151ac939723Sdormando         accept_new_conns(true);
152ac939723Sdormando     }
153ac939723Sdormando }
154ac939723Sdormando 
155ce1abe35SAnatoly Vorobey #define REALTIME_MAXDELTA 60*60*24*30
156eda68b70STrond Norbye 
15777dde9f9SPaul Lindner /*
15877dde9f9SPaul Lindner  * given time value that's either unix time or delta from current unix time, return
15977dde9f9SPaul Lindner  * unix time. Use the fact that delta can't exceed one month (and real time value can't
16077dde9f9SPaul Lindner  * be that low).
16177dde9f9SPaul Lindner  */
realtime(const time_t exptime)16277dde9f9SPaul Lindner static rel_time_t realtime(const time_t exptime) {
163c9607c6dSBrad Fitzpatrick     /* no. of seconds in 30 days - largest possible delta exptime */
16486969ea4SBrad Fitzpatrick 
165bfebefb7SBrad Fitzpatrick     if (exptime == 0) return 0; /* 0 means never expire */
16686969ea4SBrad Fitzpatrick 
16742fda7d0SAdam Dixon     if (exptime > REALTIME_MAXDELTA) {
16842fda7d0SAdam Dixon         /* if item expiration is at/before the server started, give it an
16942fda7d0SAdam Dixon            expiration time of 1 second after the server started.
17042fda7d0SAdam Dixon            (because 0 means don't expire).  without this, we'd
17142fda7d0SAdam Dixon            underflow and wrap around to some large value way in the
17242fda7d0SAdam Dixon            future, effectively making items expiring in the past
17342fda7d0SAdam Dixon            really expiring never */
1741fdfb7e9STrond Norbye         if (exptime <= process_started)
17542fda7d0SAdam Dixon             return (rel_time_t)1;
1761fdfb7e9STrond Norbye         return (rel_time_t)(exptime - process_started);
17742fda7d0SAdam Dixon     } else {
178c9607c6dSBrad Fitzpatrick         return (rel_time_t)(exptime + current_time);
179bfebefb7SBrad Fitzpatrick     }
180ce1abe35SAnatoly Vorobey }
18186969ea4SBrad Fitzpatrick 
stats_init(void)18277dde9f9SPaul Lindner static void stats_init(void) {
183cb01d504Sdormando     memset(&stats, 0, sizeof(struct stats));
184cb01d504Sdormando     memset(&stats_state, 0, sizeof(struct stats_state));
185cb01d504Sdormando     stats_state.accepting_conns = true; /* assuming we start in this state. */
18660267758SBrad Fitzpatrick 
187c5944dc2SSteven Grimm     /* make the time we started always be 2 seconds before we really
18860267758SBrad Fitzpatrick        did, so time(0) - time.started is never zero.  if so, things
18960267758SBrad Fitzpatrick        like 'settings.oldest_live' which act as booleans as well as
19060267758SBrad Fitzpatrick        values are now false in boolean context... */
191d7324b0bSdormando     process_started = time(0) - ITEM_UPDATE_INTERVAL - 2;
19256b8339eSSteven Grimm     stats_prefix_init();
19332f382b6SBrad Fitzpatrick }
194217dcce0SSteven Grimm 
stats_reset(void)19577dde9f9SPaul Lindner static void stats_reset(void) {
19656b8339eSSteven Grimm     STATS_LOCK();
197cb01d504Sdormando     memset(&stats, 0, sizeof(struct stats));
19856b8339eSSteven Grimm     stats_prefix_clear();
19956b8339eSSteven Grimm     STATS_UNLOCK();
2001fdfb7e9STrond Norbye     threadlocal_stats_reset();
20153180103STrond Norbye     item_stats_reset();
20232f382b6SBrad Fitzpatrick }
20386969ea4SBrad Fitzpatrick 
settings_init(void)20477dde9f9SPaul Lindner static void settings_init(void) {
205eda68b70STrond Norbye     settings.use_cas = true;
20640c76cedSDavid Bremner     settings.access = 0700;
20732f382b6SBrad Fitzpatrick     settings.port = 11211;
208b3723633SBrian Aker     settings.udpport = 11211;
209a61a6900SBrian Aker     /* By default this string should be NULL for getaddrinfo() */
210a61a6900SBrian Aker     settings.inter = NULL;
21195ddc95fSSteven Grimm     settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */
2126d74e134SBrad Fitzpatrick     settings.maxconns = 1024;         /* to limit connections-related memory to about 5MB */
21360d70942SAnatoly Vorobey     settings.verbose = 0;
214d9f93589SAnatoly Vorobey     settings.oldest_live = 0;
21590593dcaSdormando     settings.oldest_cas = 0;          /* supplements accuracy of oldest_live */
216841811e9SJason Titus     settings.evict_to_free = 1;       /* push old items out of cache when memory runs out */
217c9607c6dSBrad Fitzpatrick     settings.socketpath = NULL;       /* by default, not using a unix socket */
218c9607c6dSBrad Fitzpatrick     settings.factor = 1.25;
219c9607c6dSBrad Fitzpatrick     settings.chunk_size = 48;         /* space for a modest key and value */
2202fe44f1cSDmitry Isaykin     settings.num_threads = 4;         /* N workers */
221c60ca35bSTrond Norbye     settings.num_threads_per_udp = 0;
22256b8339eSSteven Grimm     settings.prefix_delimiter = ':';
22356b8339eSSteven Grimm     settings.detail_enabled = 0;
224ca90710fSdormando     settings.reqs_per_event = 20;
2257d010a85SChris Goffinet     settings.backlog = 1024;
226a155b044SDustin Sallings     settings.binding_protocol = negotiating_prot;
227bed5f9bbSdormando     settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */
22851a828b9Sdormando     settings.slab_chunk_size_max = settings.item_size_max;
2290567967aSdormando     settings.slab_page_size = 1024 * 1024; /* chunks are split from 1MB pages. */
230e15e1d6bS祁冰     settings.sasl = false;
231d1f9d992Sdormando     settings.maxconns_fast = false;
232d425b35bSdormando     settings.lru_crawler = false;
23331d533f8Sdormando     settings.lru_crawler_sleep = 100;
234e31a5912Sdormando     settings.lru_crawler_tocrawl = 0;
235d9edfefdSdormando     settings.lru_maintainer_thread = false;
2368d6bf78aSdormando     settings.hot_lru_pct = 32;
2378d6bf78aSdormando     settings.warm_lru_pct = 32;
2384de89c8cSdormando     settings.expirezero_does_not_evict = false;
23983ba6bd9SJay Grizzard     settings.idle_timeout = 0; /* disabled */
2401db1de38Sdormando     settings.hashpower_init = 0;
24110698baeSdormando     settings.slab_reassign = false;
24263bf748aSdormando     settings.slab_automove = 0;
243d11dc0eaSBrian Aker     settings.shutdown_command = false;
244058af0d8SKeyur     settings.tail_repair_time = TAIL_REPAIR_TIME_DEFAULT;
245a2f5ca50SDaniel Pañeda     settings.flush_enabled = true;
24687ff9dc0Sdormando     settings.crawls_persleep = 1000;
247d704f2c0Sdormando     settings.logger_watcher_buf_size = LOGGER_WATCHER_BUF_SIZE;
248d704f2c0Sdormando     settings.logger_buf_size = LOGGER_BUF_SIZE;
249c9607c6dSBrad Fitzpatrick }
25020892be0SBrad Fitzpatrick 
251c9607c6dSBrad Fitzpatrick /*
252c9607c6dSBrad Fitzpatrick  * Adds a message header to a connection.
253c9607c6dSBrad Fitzpatrick  *
254c9607c6dSBrad Fitzpatrick  * Returns 0 on success, -1 on out-of-memory.
255c9607c6dSBrad Fitzpatrick  */
add_msghdr(conn * c)25677dde9f9SPaul Lindner static int add_msghdr(conn *c)
257c9607c6dSBrad Fitzpatrick {
258c9607c6dSBrad Fitzpatrick     struct msghdr *msg;
25986969ea4SBrad Fitzpatrick 
26078955139STim Yardley     assert(c != NULL);
26178955139STim Yardley 
262c9607c6dSBrad Fitzpatrick     if (c->msgsize == c->msgused) {
263c9607c6dSBrad Fitzpatrick         msg = realloc(c->msglist, c->msgsize * 2 * sizeof(struct msghdr));
264de021a9cSTrond Norbye         if (! msg) {
265de021a9cSTrond Norbye             STATS_LOCK();
266de021a9cSTrond Norbye             stats.malloc_fails++;
267de021a9cSTrond Norbye             STATS_UNLOCK();
268c9607c6dSBrad Fitzpatrick             return -1;
269de021a9cSTrond Norbye         }
270c9607c6dSBrad Fitzpatrick         c->msglist = msg;
271c9607c6dSBrad Fitzpatrick         c->msgsize *= 2;
272c9607c6dSBrad Fitzpatrick     }
27386969ea4SBrad Fitzpatrick 
274c9607c6dSBrad Fitzpatrick     msg = c->msglist + c->msgused;
275edbccb0fSBrad Fitzpatrick 
276edbccb0fSBrad Fitzpatrick     /* this wipes msg_iovlen, msg_control, msg_controllen, and
277edbccb0fSBrad Fitzpatrick        msg_flags, the last 3 of which aren't defined on solaris: */
278edbccb0fSBrad Fitzpatrick     memset(msg, 0, sizeof(struct msghdr));
279edbccb0fSBrad Fitzpatrick 
280c9607c6dSBrad Fitzpatrick     msg->msg_iov = &c->iov[c->iovused];
28152d1cf20SMaxim Dounin 
28270c1b5f6SSteven Grimm     if (IS_UDP(c->transport) && c->request_addr_size > 0) {
283c9607c6dSBrad Fitzpatrick         msg->msg_name = &c->request_addr;
284c9607c6dSBrad Fitzpatrick         msg->msg_namelen = c->request_addr_size;
28552d1cf20SMaxim Dounin     }
286edbccb0fSBrad Fitzpatrick 
287c9607c6dSBrad Fitzpatrick     c->msgbytes = 0;
288c9607c6dSBrad Fitzpatrick     c->msgused++;
28986969ea4SBrad Fitzpatrick 
29015ace4b5SEric Lambert     if (IS_UDP(c->transport)) {
291c9607c6dSBrad Fitzpatrick         /* Leave room for the UDP header, which we'll fill in later. */
292c9607c6dSBrad Fitzpatrick         return add_iov(c, NULL, UDP_HEADER_SIZE);
293c9607c6dSBrad Fitzpatrick     }
29486969ea4SBrad Fitzpatrick 
295c9607c6dSBrad Fitzpatrick     return 0;
29632f382b6SBrad Fitzpatrick }
29786969ea4SBrad Fitzpatrick 
29870c1b5f6SSteven Grimm extern pthread_mutex_t conn_lock;
29956b8339eSSteven Grimm 
30083ba6bd9SJay Grizzard /* Connection timeout thread bits */
30183ba6bd9SJay Grizzard static pthread_t conn_timeout_tid;
30283ba6bd9SJay Grizzard 
30383ba6bd9SJay Grizzard #define CONNS_PER_SLICE 100
30483ba6bd9SJay Grizzard #define TIMEOUT_MSG_SIZE (1 + sizeof(int))
conn_timeout_thread(void * arg)30583ba6bd9SJay Grizzard static void *conn_timeout_thread(void *arg) {
30683ba6bd9SJay Grizzard     int i;
30783ba6bd9SJay Grizzard     conn *c;
30883ba6bd9SJay Grizzard     char buf[TIMEOUT_MSG_SIZE];
30983ba6bd9SJay Grizzard     rel_time_t oldest_last_cmd;
31083ba6bd9SJay Grizzard     int sleep_time;
31183ba6bd9SJay Grizzard     useconds_t timeslice = 1000000 / (max_fds / CONNS_PER_SLICE);
31283ba6bd9SJay Grizzard 
31383ba6bd9SJay Grizzard     while(1) {
31483ba6bd9SJay Grizzard         if (settings.verbose > 2)
31583ba6bd9SJay Grizzard             fprintf(stderr, "idle timeout thread at top of connection list\n");
31683ba6bd9SJay Grizzard 
31783ba6bd9SJay Grizzard         oldest_last_cmd = current_time;
31883ba6bd9SJay Grizzard 
31983ba6bd9SJay Grizzard         for (i = 0; i < max_fds; i++) {
32083ba6bd9SJay Grizzard             if ((i % CONNS_PER_SLICE) == 0) {
32183ba6bd9SJay Grizzard                 if (settings.verbose > 2)
32283ba6bd9SJay Grizzard                     fprintf(stderr, "idle timeout thread sleeping for %dus\n",
32383ba6bd9SJay Grizzard                         timeslice);
32483ba6bd9SJay Grizzard                 usleep(timeslice);
32583ba6bd9SJay Grizzard             }
32683ba6bd9SJay Grizzard 
32783ba6bd9SJay Grizzard             if (!conns[i])
32883ba6bd9SJay Grizzard                 continue;
32983ba6bd9SJay Grizzard 
33083ba6bd9SJay Grizzard             c = conns[i];
33183ba6bd9SJay Grizzard 
33283ba6bd9SJay Grizzard             if (!IS_TCP(c->transport))
33383ba6bd9SJay Grizzard                 continue;
33483ba6bd9SJay Grizzard 
33583ba6bd9SJay Grizzard             if (c->state != conn_new_cmd && c->state != conn_read)
33683ba6bd9SJay Grizzard                 continue;
33783ba6bd9SJay Grizzard 
33883ba6bd9SJay Grizzard             if ((current_time - c->last_cmd_time) > settings.idle_timeout) {
33983ba6bd9SJay Grizzard                 buf[0] = 't';
34083ba6bd9SJay Grizzard                 memcpy(&buf[1], &i, sizeof(int));
34183ba6bd9SJay Grizzard                 if (write(c->thread->notify_send_fd, buf, TIMEOUT_MSG_SIZE)
34283ba6bd9SJay Grizzard                     != TIMEOUT_MSG_SIZE)
34383ba6bd9SJay Grizzard                     perror("Failed to write timeout to notify pipe");
34483ba6bd9SJay Grizzard             } else {
34583ba6bd9SJay Grizzard                 if (c->last_cmd_time < oldest_last_cmd)
34683ba6bd9SJay Grizzard                     oldest_last_cmd = c->last_cmd_time;
34783ba6bd9SJay Grizzard             }
34883ba6bd9SJay Grizzard         }
34983ba6bd9SJay Grizzard 
35083ba6bd9SJay Grizzard         /* This is the soonest we could have another connection time out */
35183ba6bd9SJay Grizzard         sleep_time = settings.idle_timeout - (current_time - oldest_last_cmd) + 1;
35283ba6bd9SJay Grizzard         if (sleep_time <= 0)
35383ba6bd9SJay Grizzard             sleep_time = 1;
35483ba6bd9SJay Grizzard 
35583ba6bd9SJay Grizzard         if (settings.verbose > 2)
35683ba6bd9SJay Grizzard             fprintf(stderr,
35783ba6bd9SJay Grizzard                     "idle timeout thread finished pass, sleeping for %ds\n",
35883ba6bd9SJay Grizzard                     sleep_time);
35983ba6bd9SJay Grizzard         usleep((useconds_t) sleep_time * 1000000);
36083ba6bd9SJay Grizzard     }
36183ba6bd9SJay Grizzard 
36283ba6bd9SJay Grizzard     return NULL;
36383ba6bd9SJay Grizzard }
36483ba6bd9SJay Grizzard 
start_conn_timeout_thread()36583ba6bd9SJay Grizzard static int start_conn_timeout_thread() {
36683ba6bd9SJay Grizzard     int ret;
36783ba6bd9SJay Grizzard 
36883ba6bd9SJay Grizzard     if (settings.idle_timeout == 0)
36983ba6bd9SJay Grizzard         return -1;
37083ba6bd9SJay Grizzard 
37183ba6bd9SJay Grizzard     if ((ret = pthread_create(&conn_timeout_tid, NULL,
37283ba6bd9SJay Grizzard         conn_timeout_thread, NULL)) != 0) {
37383ba6bd9SJay Grizzard         fprintf(stderr, "Can't create idle connection timeout thread: %s\n",
37483ba6bd9SJay Grizzard             strerror(ret));
37583ba6bd9SJay Grizzard         return -1;
37683ba6bd9SJay Grizzard     }
37783ba6bd9SJay Grizzard 
37883ba6bd9SJay Grizzard     return 0;
37983ba6bd9SJay Grizzard }
38083ba6bd9SJay Grizzard 
38156b8339eSSteven Grimm /*
38270c1b5f6SSteven Grimm  * Initializes the connections array. We don't actually allocate connection
38370c1b5f6SSteven Grimm  * structures until they're needed, so as to avoid wasting memory when the
38470c1b5f6SSteven Grimm  * maximum connection count is much higher than the actual number of
38570c1b5f6SSteven Grimm  * connections.
38670c1b5f6SSteven Grimm  *
38770c1b5f6SSteven Grimm  * This does end up wasting a few pointers' worth of memory for FDs that are
38870c1b5f6SSteven Grimm  * used for things other than connections, but that's worth it in exchange for
38970c1b5f6SSteven Grimm  * being able to directly index the conns array by FD.
39056b8339eSSteven Grimm  */
conn_init(void)39177dde9f9SPaul Lindner static void conn_init(void) {
39270c1b5f6SSteven Grimm     /* We're unlikely to see an FD much higher than maxconns. */
39370c1b5f6SSteven Grimm     int next_fd = dup(1);
39470c1b5f6SSteven Grimm     int headroom = 10;      /* account for extra unexpected open FDs */
39570c1b5f6SSteven Grimm     struct rlimit rl;
39670c1b5f6SSteven Grimm 
39770c1b5f6SSteven Grimm     max_fds = settings.maxconns + headroom + next_fd;
39870c1b5f6SSteven Grimm 
39970c1b5f6SSteven Grimm     /* But if possible, get the actual highest FD we can possibly ever see. */
40070c1b5f6SSteven Grimm     if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
40170c1b5f6SSteven Grimm         max_fds = rl.rlim_max;
40270c1b5f6SSteven Grimm     } else {
40370c1b5f6SSteven Grimm         fprintf(stderr, "Failed to query maximum file descriptor; "
40470c1b5f6SSteven Grimm                         "falling back to maxconns\n");
40570c1b5f6SSteven Grimm     }
40670c1b5f6SSteven Grimm 
40770c1b5f6SSteven Grimm     close(next_fd);
40870c1b5f6SSteven Grimm 
40970c1b5f6SSteven Grimm     if ((conns = calloc(max_fds, sizeof(conn *))) == NULL) {
410a4562105STrond Norbye         fprintf(stderr, "Failed to allocate connection structures\n");
41170c1b5f6SSteven Grimm         /* This is unrecoverable so bail out early. */
41270c1b5f6SSteven Grimm         exit(1);
41378955139STim Yardley     }
41456b8339eSSteven Grimm }
41556b8339eSSteven Grimm 
prot_text(enum protocol prot)416f1351f9bSTrond Norbye static const char *prot_text(enum protocol prot) {
4170a3f8c74SDustin Sallings     char *rv = "unknown";
4180a3f8c74SDustin Sallings     switch(prot) {
4190a3f8c74SDustin Sallings         case ascii_prot:
4200a3f8c74SDustin Sallings             rv = "ascii";
4210a3f8c74SDustin Sallings             break;
4220a3f8c74SDustin Sallings         case binary_prot:
4230a3f8c74SDustin Sallings             rv = "binary";
4240a3f8c74SDustin Sallings             break;
425cba691f4SDustin Sallings         case negotiating_prot:
426cba691f4SDustin Sallings             rv = "auto-negotiate";
427cba691f4SDustin Sallings             break;
4280a3f8c74SDustin Sallings     }
4290a3f8c74SDustin Sallings     return rv;
4300a3f8c74SDustin Sallings }
4310a3f8c74SDustin Sallings 
conn_close_idle(conn * c)43283ba6bd9SJay Grizzard void conn_close_idle(conn *c) {
43383ba6bd9SJay Grizzard     if (settings.idle_timeout > 0 &&
43483ba6bd9SJay Grizzard         (current_time - c->last_cmd_time) > settings.idle_timeout) {
43583ba6bd9SJay Grizzard         if (c->state != conn_new_cmd && c->state != conn_read) {
43683ba6bd9SJay Grizzard             if (settings.verbose > 1)
43783ba6bd9SJay Grizzard                 fprintf(stderr,
43883ba6bd9SJay Grizzard                     "fd %d wants to timeout, but isn't in read state", c->sfd);
43983ba6bd9SJay Grizzard             return;
44083ba6bd9SJay Grizzard         }
44183ba6bd9SJay Grizzard 
44283ba6bd9SJay Grizzard         if (settings.verbose > 1)
44383ba6bd9SJay Grizzard             fprintf(stderr, "Closing idle fd %d\n", c->sfd);
44483ba6bd9SJay Grizzard 
44583ba6bd9SJay Grizzard         c->thread->stats.idle_kicks++;
44683ba6bd9SJay Grizzard 
44783ba6bd9SJay Grizzard         conn_set_state(c, conn_closing);
44883ba6bd9SJay Grizzard         drive_machine(c);
44983ba6bd9SJay Grizzard     }
45083ba6bd9SJay Grizzard }
45183ba6bd9SJay Grizzard 
conn_new(const int sfd,enum conn_states init_state,const int event_flags,const int read_buffer_size,enum network_transport transport,struct event_base * base)4523ed60cddSDustin Sallings conn *conn_new(const int sfd, enum conn_states init_state,
4533ed60cddSDustin Sallings                 const int event_flags,
45415ace4b5SEric Lambert                 const int read_buffer_size, enum network_transport transport,
4556aafe58eSDustin Sallings                 struct event_base *base) {
45670c1b5f6SSteven Grimm     conn *c;
45770c1b5f6SSteven Grimm 
45870c1b5f6SSteven Grimm     assert(sfd >= 0 && sfd < max_fds);
45970c1b5f6SSteven Grimm     c = conns[sfd];
46056b8339eSSteven Grimm 
46156b8339eSSteven Grimm     if (NULL == c) {
462df05ca59SDustin Sallings         if (!(c = (conn *)calloc(1, sizeof(conn)))) {
463de021a9cSTrond Norbye             STATS_LOCK();
464de021a9cSTrond Norbye             stats.malloc_fails++;
465de021a9cSTrond Norbye             STATS_UNLOCK();
466de021a9cSTrond Norbye             fprintf(stderr, "Failed to allocate connection object\n");
46777dde9f9SPaul Lindner             return NULL;
46832f382b6SBrad Fitzpatrick         }
46968957214STrond Norbye         MEMCACHED_CONN_CREATE(c);
47068957214STrond Norbye 
47132f382b6SBrad Fitzpatrick         c->rbuf = c->wbuf = 0;
47232f382b6SBrad Fitzpatrick         c->ilist = 0;
473e61c0a86Sdormando         c->suffixlist = 0;
474c9607c6dSBrad Fitzpatrick         c->iov = 0;
475c9607c6dSBrad Fitzpatrick         c->msglist = 0;
476c9607c6dSBrad Fitzpatrick         c->hdrbuf = 0;
47786969ea4SBrad Fitzpatrick 
478c9607c6dSBrad Fitzpatrick         c->rsize = read_buffer_size;
479c9607c6dSBrad Fitzpatrick         c->wsize = DATA_BUFFER_SIZE;
480c9607c6dSBrad Fitzpatrick         c->isize = ITEM_LIST_INITIAL;
481e61c0a86Sdormando         c->suffixsize = SUFFIX_LIST_INITIAL;
482c9607c6dSBrad Fitzpatrick         c->iovsize = IOV_LIST_INITIAL;
483c9607c6dSBrad Fitzpatrick         c->msgsize = MSG_LIST_INITIAL;
484c9607c6dSBrad Fitzpatrick         c->hdrsize = 0;
48586969ea4SBrad Fitzpatrick 
48677dde9f9SPaul Lindner         c->rbuf = (char *)malloc((size_t)c->rsize);
48777dde9f9SPaul Lindner         c->wbuf = (char *)malloc((size_t)c->wsize);
488c9607c6dSBrad Fitzpatrick         c->ilist = (item **)malloc(sizeof(item *) * c->isize);
489e61c0a86Sdormando         c->suffixlist = (char **)malloc(sizeof(char *) * c->suffixsize);
490c9607c6dSBrad Fitzpatrick         c->iov = (struct iovec *)malloc(sizeof(struct iovec) * c->iovsize);
491c9607c6dSBrad Fitzpatrick         c->msglist = (struct msghdr *)malloc(sizeof(struct msghdr) * c->msgsize);
49286969ea4SBrad Fitzpatrick 
493c9607c6dSBrad Fitzpatrick         if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0 || c->iov == 0 ||
494e61c0a86Sdormando                 c->msglist == 0 || c->suffixlist == 0) {
49568957214STrond Norbye             conn_free(c);
496de021a9cSTrond Norbye             STATS_LOCK();
497de021a9cSTrond Norbye             stats.malloc_fails++;
498de021a9cSTrond Norbye             STATS_UNLOCK();
499de021a9cSTrond Norbye             fprintf(stderr, "Failed to allocate buffers for connection\n");
50077dde9f9SPaul Lindner             return NULL;
50132f382b6SBrad Fitzpatrick         }
50286969ea4SBrad Fitzpatrick 
50356b8339eSSteven Grimm         STATS_LOCK();
504cb01d504Sdormando         stats_state.conn_structs++;
50556b8339eSSteven Grimm         STATS_UNLOCK();
50670c1b5f6SSteven Grimm 
50770c1b5f6SSteven Grimm         c->sfd = sfd;
50870c1b5f6SSteven Grimm         conns[sfd] = c;
50960d70942SAnatoly Vorobey     }
51086969ea4SBrad Fitzpatrick 
51115ace4b5SEric Lambert     c->transport = transport;
512a155b044SDustin Sallings     c->protocol = settings.binding_protocol;
51315ace4b5SEric Lambert 
5146aafe58eSDustin Sallings     /* unix socket mode doesn't need this, so zeroed out.  but why
5156aafe58eSDustin Sallings      * is this done for every command?  presumably for UDP
5166aafe58eSDustin Sallings      * mode.  */
5176aafe58eSDustin Sallings     if (!settings.socketpath) {
5186aafe58eSDustin Sallings         c->request_addr_size = sizeof(c->request_addr);
5196aafe58eSDustin Sallings     } else {
5206aafe58eSDustin Sallings         c->request_addr_size = 0;
5216aafe58eSDustin Sallings     }
5226aafe58eSDustin Sallings 
52370c1b5f6SSteven Grimm     if (transport == tcp_transport && init_state == conn_new_cmd) {
52470c1b5f6SSteven Grimm         if (getpeername(sfd, (struct sockaddr *) &c->request_addr,
52570c1b5f6SSteven Grimm                         &c->request_addr_size)) {
52670c1b5f6SSteven Grimm             perror("getpeername");
52770c1b5f6SSteven Grimm             memset(&c->request_addr, 0, sizeof(c->request_addr));
52870c1b5f6SSteven Grimm         }
52970c1b5f6SSteven Grimm     }
53070c1b5f6SSteven Grimm 
53116a4b555SBrad Fitzpatrick     if (settings.verbose > 1) {
5326aafe58eSDustin Sallings         if (init_state == conn_listening) {
5330a3f8c74SDustin Sallings             fprintf(stderr, "<%d server listening (%s)\n", sfd,
53415ace4b5SEric Lambert                 prot_text(c->protocol));
53515ace4b5SEric Lambert         } else if (IS_UDP(transport)) {
536c9607c6dSBrad Fitzpatrick             fprintf(stderr, "<%d server listening (udp)\n", sfd);
53715ace4b5SEric Lambert         } else if (c->protocol == negotiating_prot) {
538f1351f9bSTrond Norbye             fprintf(stderr, "<%d new auto-negotiating client connection\n",
539f1351f9bSTrond Norbye                     sfd);
540c666e344SDustin Sallings         } else if (c->protocol == ascii_prot) {
541c666e344SDustin Sallings             fprintf(stderr, "<%d new ascii client connection.\n", sfd);
542c666e344SDustin Sallings         } else if (c->protocol == binary_prot) {
543c666e344SDustin Sallings             fprintf(stderr, "<%d new binary client connection.\n", sfd);
5446aafe58eSDustin Sallings         } else {
5456aafe58eSDustin Sallings             fprintf(stderr, "<%d new unknown (%d) client connection\n",
54615ace4b5SEric Lambert                 sfd, c->protocol);
547c666e344SDustin Sallings             assert(false);
5486aafe58eSDustin Sallings         }
54916a4b555SBrad Fitzpatrick     }
55086969ea4SBrad Fitzpatrick 
55132f382b6SBrad Fitzpatrick     c->state = init_state;
55232f382b6SBrad Fitzpatrick     c->rlbytes = 0;
5536aafe58eSDustin Sallings     c->cmd = -1;
55432f382b6SBrad Fitzpatrick     c->rbytes = c->wbytes = 0;
55532f382b6SBrad Fitzpatrick     c->wcurr = c->wbuf;
556c9607c6dSBrad Fitzpatrick     c->rcurr = c->rbuf;
5577a308025SAnatoly Vorobey     c->ritem = 0;
55832f382b6SBrad Fitzpatrick     c->icurr = c->ilist;
559e61c0a86Sdormando     c->suffixcurr = c->suffixlist;
56032f382b6SBrad Fitzpatrick     c->ileft = 0;
561e61c0a86Sdormando     c->suffixleft = 0;
562c9607c6dSBrad Fitzpatrick     c->iovused = 0;
563c9607c6dSBrad Fitzpatrick     c->msgcurr = 0;
564c9607c6dSBrad Fitzpatrick     c->msgused = 0;
56587c1cf0fS伊藤洋也     c->authenticated = false;
56683ba6bd9SJay Grizzard     c->last_cmd_time = current_time; /* initialize for idle kicker */
56786969ea4SBrad Fitzpatrick 
5686aafe58eSDustin Sallings     c->write_and_go = init_state;
56932f382b6SBrad Fitzpatrick     c->write_and_free = 0;
57032f382b6SBrad Fitzpatrick     c->item = 0;
57186969ea4SBrad Fitzpatrick 
572d9ece780STomash Brechko     c->noreply = false;
573d9ece780STomash Brechko 
57432f382b6SBrad Fitzpatrick     event_set(&c->event, sfd, event_flags, event_handler, (void *)c);
57556b8339eSSteven Grimm     event_base_set(base, &c->event);
57632f382b6SBrad Fitzpatrick     c->ev_flags = event_flags;
57786969ea4SBrad Fitzpatrick 
57832f382b6SBrad Fitzpatrick     if (event_add(&c->event, 0) == -1) {
5799150c85bSBrian Aker         perror("event_add");
58077dde9f9SPaul Lindner         return NULL;
58132f382b6SBrad Fitzpatrick     }
58286969ea4SBrad Fitzpatrick 
58356b8339eSSteven Grimm     STATS_LOCK();
584cb01d504Sdormando     stats_state.curr_conns++;
58532f382b6SBrad Fitzpatrick     stats.total_conns++;
58656b8339eSSteven Grimm     STATS_UNLOCK();
58786969ea4SBrad Fitzpatrick 
58868957214STrond Norbye     MEMCACHED_CONN_ALLOCATE(c->sfd);
58968957214STrond Norbye 
59032f382b6SBrad Fitzpatrick     return c;
59132f382b6SBrad Fitzpatrick }
59286969ea4SBrad Fitzpatrick 
conn_release_items(conn * c)59389d0d726SSteven Grimm static void conn_release_items(conn *c) {
59478955139STim Yardley     assert(c != NULL);
59578955139STim Yardley 
59632f382b6SBrad Fitzpatrick     if (c->item) {
59756b8339eSSteven Grimm         item_remove(c->item);
598c9607c6dSBrad Fitzpatrick         c->item = 0;
59932f382b6SBrad Fitzpatrick     }
60086969ea4SBrad Fitzpatrick 
60189d0d726SSteven Grimm     while (c->ileft > 0) {
60289d0d726SSteven Grimm         item *it = *(c->icurr);
60389d0d726SSteven Grimm         assert((it->it_flags & ITEM_SLABBED) == 0);
60489d0d726SSteven Grimm         item_remove(it);
60589d0d726SSteven Grimm         c->icurr++;
60689d0d726SSteven Grimm         c->ileft--;
60732f382b6SBrad Fitzpatrick     }
60886969ea4SBrad Fitzpatrick 
609e61c0a86Sdormando     if (c->suffixleft != 0) {
610e61c0a86Sdormando         for (; c->suffixleft > 0; c->suffixleft--, c->suffixcurr++) {
6114c86fa59STrond Norbye             cache_free(c->thread->suffix_cache, *(c->suffixcurr));
612e61c0a86Sdormando         }
613e61c0a86Sdormando     }
614e61c0a86Sdormando 
61589d0d726SSteven Grimm     c->icurr = c->ilist;
61689d0d726SSteven Grimm     c->suffixcurr = c->suffixlist;
61789d0d726SSteven Grimm }
61889d0d726SSteven Grimm 
conn_cleanup(conn * c)61989d0d726SSteven Grimm static void conn_cleanup(conn *c) {
62089d0d726SSteven Grimm     assert(c != NULL);
62189d0d726SSteven Grimm 
62289d0d726SSteven Grimm     conn_release_items(c);
62389d0d726SSteven Grimm 
62432f382b6SBrad Fitzpatrick     if (c->write_and_free) {
62532f382b6SBrad Fitzpatrick         free(c->write_and_free);
626c9607c6dSBrad Fitzpatrick         c->write_and_free = 0;
627c9607c6dSBrad Fitzpatrick     }
628f1307c4dSDustin Sallings 
629f1307c4dSDustin Sallings     if (c->sasl_conn) {
630f1307c4dSDustin Sallings         assert(settings.sasl);
631f1307c4dSDustin Sallings         sasl_dispose(&c->sasl_conn);
632f1307c4dSDustin Sallings         c->sasl_conn = NULL;
633f1307c4dSDustin Sallings     }
6342928a785Sdormando 
6352928a785Sdormando     if (IS_UDP(c->transport)) {
6362928a785Sdormando         conn_set_state(c, conn_read);
6372928a785Sdormando     }
63832f382b6SBrad Fitzpatrick }
63986969ea4SBrad Fitzpatrick 
640c9607c6dSBrad Fitzpatrick /*
641c9607c6dSBrad Fitzpatrick  * Frees a connection.
642c9607c6dSBrad Fitzpatrick  */
conn_free(conn * c)64356b8339eSSteven Grimm void conn_free(conn *c) {
644c9607c6dSBrad Fitzpatrick     if (c) {
64570c1b5f6SSteven Grimm         assert(c != NULL);
64670c1b5f6SSteven Grimm         assert(c->sfd >= 0 && c->sfd < max_fds);
64770c1b5f6SSteven Grimm 
64868957214STrond Norbye         MEMCACHED_CONN_DESTROY(c);
64970c1b5f6SSteven Grimm         conns[c->sfd] = NULL;
650c9607c6dSBrad Fitzpatrick         if (c->hdrbuf)
651c9607c6dSBrad Fitzpatrick             free(c->hdrbuf);
652c9607c6dSBrad Fitzpatrick         if (c->msglist)
653c9607c6dSBrad Fitzpatrick             free(c->msglist);
654c9607c6dSBrad Fitzpatrick         if (c->rbuf)
655c9607c6dSBrad Fitzpatrick             free(c->rbuf);
656c9607c6dSBrad Fitzpatrick         if (c->wbuf)
657c9607c6dSBrad Fitzpatrick             free(c->wbuf);
658c9607c6dSBrad Fitzpatrick         if (c->ilist)
659c9607c6dSBrad Fitzpatrick             free(c->ilist);
660e61c0a86Sdormando         if (c->suffixlist)
661e61c0a86Sdormando             free(c->suffixlist);
662c9607c6dSBrad Fitzpatrick         if (c->iov)
663c9607c6dSBrad Fitzpatrick             free(c->iov);
664c9607c6dSBrad Fitzpatrick         free(c);
665c9607c6dSBrad Fitzpatrick     }
666c9607c6dSBrad Fitzpatrick }
66786969ea4SBrad Fitzpatrick 
conn_close(conn * c)66877dde9f9SPaul Lindner static void conn_close(conn *c) {
66978955139STim Yardley     assert(c != NULL);
67078955139STim Yardley 
671c9607c6dSBrad Fitzpatrick     /* delete the event, the socket and the conn */
672c9607c6dSBrad Fitzpatrick     event_del(&c->event);
67386969ea4SBrad Fitzpatrick 
674c9607c6dSBrad Fitzpatrick     if (settings.verbose > 1)
675c9607c6dSBrad Fitzpatrick         fprintf(stderr, "<%d connection closed.\n", c->sfd);
67686969ea4SBrad Fitzpatrick 
67770c1b5f6SSteven Grimm     conn_cleanup(c);
67870c1b5f6SSteven Grimm 
67968957214STrond Norbye     MEMCACHED_CONN_RELEASE(c->sfd);
68070c1b5f6SSteven Grimm     conn_set_state(c, conn_closed);
681e73bc2e5Sdormando     close(c->sfd);
68270c1b5f6SSteven Grimm 
683ac939723Sdormando     pthread_mutex_lock(&conn_lock);
684ac939723Sdormando     allow_new_conns = true;
685ac939723Sdormando     pthread_mutex_unlock(&conn_lock);
68686969ea4SBrad Fitzpatrick 
68756b8339eSSteven Grimm     STATS_LOCK();
688cb01d504Sdormando     stats_state.curr_conns--;
68956b8339eSSteven Grimm     STATS_UNLOCK();
69086969ea4SBrad Fitzpatrick 
69132f382b6SBrad Fitzpatrick     return;
69232f382b6SBrad Fitzpatrick }
69386969ea4SBrad Fitzpatrick 
694c9607c6dSBrad Fitzpatrick /*
695c9607c6dSBrad Fitzpatrick  * Shrinks a connection's buffers if they're too big.  This prevents
696c9607c6dSBrad Fitzpatrick  * periodic large "get" requests from permanently chewing lots of server
697c9607c6dSBrad Fitzpatrick  * memory.
698c9607c6dSBrad Fitzpatrick  *
699c9607c6dSBrad Fitzpatrick  * This should only be called in between requests since it can wipe output
700c9607c6dSBrad Fitzpatrick  * buffers!
701c9607c6dSBrad Fitzpatrick  */
conn_shrink(conn * c)7022b551b06SPaul Lindner static void conn_shrink(conn *c) {
70378955139STim Yardley     assert(c != NULL);
70478955139STim Yardley 
70515ace4b5SEric Lambert     if (IS_UDP(c->transport))
706c9607c6dSBrad Fitzpatrick         return;
70786969ea4SBrad Fitzpatrick 
708c9607c6dSBrad Fitzpatrick     if (c->rsize > READ_BUFFER_HIGHWAT && c->rbytes < DATA_BUFFER_SIZE) {
709c0ec7b09SPaolo Borelli         char *newbuf;
710c0ec7b09SPaolo Borelli 
711217dcce0SSteven Grimm         if (c->rcurr != c->rbuf)
71277dde9f9SPaul Lindner             memmove(c->rbuf, c->rcurr, (size_t)c->rbytes);
7132b551b06SPaul Lindner 
714c0ec7b09SPaolo Borelli         newbuf = (char *)realloc((void *)c->rbuf, DATA_BUFFER_SIZE);
71577dde9f9SPaul Lindner 
7162b551b06SPaul Lindner         if (newbuf) {
7172b551b06SPaul Lindner             c->rbuf = newbuf;
7182b551b06SPaul Lindner             c->rsize = DATA_BUFFER_SIZE;
7192b551b06SPaul Lindner         }
7202b551b06SPaul Lindner         /* TODO check other branch... */
721217dcce0SSteven Grimm         c->rcurr = c->rbuf;
722c9607c6dSBrad Fitzpatrick     }
72386969ea4SBrad Fitzpatrick 
724c9607c6dSBrad Fitzpatrick     if (c->isize > ITEM_LIST_HIGHWAT) {
7258bb08bc2SThomas van Gulick         item **newbuf = (item**) realloc((void *)c->ilist, ITEM_LIST_INITIAL * sizeof(c->ilist[0]));
7262b551b06SPaul Lindner         if (newbuf) {
7272b551b06SPaul Lindner             c->ilist = newbuf;
7282b551b06SPaul Lindner             c->isize = ITEM_LIST_INITIAL;
7292b551b06SPaul Lindner         }
7302b551b06SPaul Lindner     /* TODO check error condition? */
731c9607c6dSBrad Fitzpatrick     }
73286969ea4SBrad Fitzpatrick 
733c9607c6dSBrad Fitzpatrick     if (c->msgsize > MSG_LIST_HIGHWAT) {
7348bb08bc2SThomas van Gulick         struct msghdr *newbuf = (struct msghdr *) realloc((void *)c->msglist, MSG_LIST_INITIAL * sizeof(c->msglist[0]));
7352b551b06SPaul Lindner         if (newbuf) {
7362b551b06SPaul Lindner             c->msglist = newbuf;
7372b551b06SPaul Lindner             c->msgsize = MSG_LIST_INITIAL;
7382b551b06SPaul Lindner         }
7392b551b06SPaul Lindner     /* TODO check error condition? */
740c9607c6dSBrad Fitzpatrick     }
74186969ea4SBrad Fitzpatrick 
742c9607c6dSBrad Fitzpatrick     if (c->iovsize > IOV_LIST_HIGHWAT) {
7438bb08bc2SThomas van Gulick         struct iovec *newbuf = (struct iovec *) realloc((void *)c->iov, IOV_LIST_INITIAL * sizeof(c->iov[0]));
7442b551b06SPaul Lindner         if (newbuf) {
7452b551b06SPaul Lindner             c->iov = newbuf;
7462b551b06SPaul Lindner             c->iovsize = IOV_LIST_INITIAL;
7472b551b06SPaul Lindner         }
7482b551b06SPaul Lindner     /* TODO check return value */
749c9607c6dSBrad Fitzpatrick     }
750c9607c6dSBrad Fitzpatrick }
75186969ea4SBrad Fitzpatrick 
752f1351f9bSTrond Norbye /**
753f1351f9bSTrond Norbye  * Convert a state name to a human readable form.
754f1351f9bSTrond Norbye  */
state_text(enum conn_states state)755f1351f9bSTrond Norbye static const char *state_text(enum conn_states state) {
756f1351f9bSTrond Norbye     const char* const statenames[] = { "conn_listening",
757f1351f9bSTrond Norbye                                        "conn_new_cmd",
758f1351f9bSTrond Norbye                                        "conn_waiting",
759f1351f9bSTrond Norbye                                        "conn_read",
760f1351f9bSTrond Norbye                                        "conn_parse_cmd",
761f1351f9bSTrond Norbye                                        "conn_write",
762f1351f9bSTrond Norbye                                        "conn_nread",
763f1351f9bSTrond Norbye                                        "conn_swallow",
764f1351f9bSTrond Norbye                                        "conn_closing",
76570c1b5f6SSteven Grimm                                        "conn_mwrite",
766916fff36Sdormando                                        "conn_closed",
767916fff36Sdormando                                        "conn_watch" };
768f1351f9bSTrond Norbye     return statenames[state];
769f1351f9bSTrond Norbye }
770f1351f9bSTrond Norbye 
771c9607c6dSBrad Fitzpatrick /*
772c9607c6dSBrad Fitzpatrick  * Sets a connection's current state in the state machine. Any special
773c9607c6dSBrad Fitzpatrick  * processing that needs to happen on certain state transitions can
774c9607c6dSBrad Fitzpatrick  * happen here.
775c9607c6dSBrad Fitzpatrick  */
conn_set_state(conn * c,enum conn_states state)7763ed60cddSDustin Sallings static void conn_set_state(conn *c, enum conn_states state) {
77778955139STim Yardley     assert(c != NULL);
778f1351f9bSTrond Norbye     assert(state >= conn_listening && state < conn_max_state);
77978955139STim Yardley 
780c9607c6dSBrad Fitzpatrick     if (state != c->state) {
781f1351f9bSTrond Norbye         if (settings.verbose > 2) {
782f1351f9bSTrond Norbye             fprintf(stderr, "%d: going from %s to %s\n",
783f1351f9bSTrond Norbye                     c->sfd, state_text(c->state),
784f1351f9bSTrond Norbye                     state_text(state));
785c9607c6dSBrad Fitzpatrick         }
786f1351f9bSTrond Norbye 
78780ec0955STrond Norbye         if (state == conn_write || state == conn_mwrite) {
78868957214STrond Norbye             MEMCACHED_PROCESS_COMMAND_END(c->sfd, c->wbuf, c->wbytes);
78968957214STrond Norbye         }
7906298b397STrond Norbye         c->state = state;
791c9607c6dSBrad Fitzpatrick     }
792c9607c6dSBrad Fitzpatrick }
79386969ea4SBrad Fitzpatrick 
794e61c0a86Sdormando /*
795c9607c6dSBrad Fitzpatrick  * Ensures that there is room for another struct iovec in a connection's
796c9607c6dSBrad Fitzpatrick  * iov list.
797c9607c6dSBrad Fitzpatrick  *
798c9607c6dSBrad Fitzpatrick  * Returns 0 on success, -1 on out-of-memory.
799c9607c6dSBrad Fitzpatrick  */
ensure_iov_space(conn * c)80077dde9f9SPaul Lindner static int ensure_iov_space(conn *c) {
80178955139STim Yardley     assert(c != NULL);
80278955139STim Yardley 
803c9607c6dSBrad Fitzpatrick     if (c->iovused >= c->iovsize) {
804c9607c6dSBrad Fitzpatrick         int i, iovnum;
805c9607c6dSBrad Fitzpatrick         struct iovec *new_iov = (struct iovec *)realloc(c->iov,
806c9607c6dSBrad Fitzpatrick                                 (c->iovsize * 2) * sizeof(struct iovec));
807de021a9cSTrond Norbye         if (! new_iov) {
808de021a9cSTrond Norbye             STATS_LOCK();
809de021a9cSTrond Norbye             stats.malloc_fails++;
810de021a9cSTrond Norbye             STATS_UNLOCK();
811c9607c6dSBrad Fitzpatrick             return -1;
812de021a9cSTrond Norbye         }
813c9607c6dSBrad Fitzpatrick         c->iov = new_iov;
814c9607c6dSBrad Fitzpatrick         c->iovsize *= 2;
81586969ea4SBrad Fitzpatrick 
816c9607c6dSBrad Fitzpatrick         /* Point all the msghdr structures at the new list. */
817c9607c6dSBrad Fitzpatrick         for (i = 0, iovnum = 0; i < c->msgused; i++) {
818c9607c6dSBrad Fitzpatrick             c->msglist[i].msg_iov = &c->iov[iovnum];
819c9607c6dSBrad Fitzpatrick             iovnum += c->msglist[i].msg_iovlen;
820c9607c6dSBrad Fitzpatrick         }
821c9607c6dSBrad Fitzpatrick     }
82286969ea4SBrad Fitzpatrick 
823c9607c6dSBrad Fitzpatrick     return 0;
824c9607c6dSBrad Fitzpatrick }
82586969ea4SBrad Fitzpatrick 
82686969ea4SBrad Fitzpatrick 
827c9607c6dSBrad Fitzpatrick /*
828c9607c6dSBrad Fitzpatrick  * Adds data to the list of pending data that will be written out to a
829c9607c6dSBrad Fitzpatrick  * connection.
830c9607c6dSBrad Fitzpatrick  *
831c9607c6dSBrad Fitzpatrick  * Returns 0 on success, -1 on out-of-memory.
832c9607c6dSBrad Fitzpatrick  */
83386969ea4SBrad Fitzpatrick 
add_iov(conn * c,const void * buf,int len)83477dde9f9SPaul Lindner static int add_iov(conn *c, const void *buf, int len) {
835c9607c6dSBrad Fitzpatrick     struct msghdr *m;
836c9607c6dSBrad Fitzpatrick     int leftover;
83777dde9f9SPaul Lindner     bool limit_to_mtu;
83886969ea4SBrad Fitzpatrick 
83978955139STim Yardley     assert(c != NULL);
84078955139STim Yardley 
841c9607c6dSBrad Fitzpatrick     do {
842c9607c6dSBrad Fitzpatrick         m = &c->msglist[c->msgused - 1];
84386969ea4SBrad Fitzpatrick 
844c9607c6dSBrad Fitzpatrick         /*
845c9607c6dSBrad Fitzpatrick          * Limit UDP packets, and the first payloads of TCP replies, to
846c9607c6dSBrad Fitzpatrick          * UDP_MAX_PAYLOAD_SIZE bytes.
847c9607c6dSBrad Fitzpatrick          */
84815ace4b5SEric Lambert         limit_to_mtu = IS_UDP(c->transport) || (1 == c->msgused);
84986969ea4SBrad Fitzpatrick 
850c9607c6dSBrad Fitzpatrick         /* We may need to start a new msghdr if this one is full. */
851c9607c6dSBrad Fitzpatrick         if (m->msg_iovlen == IOV_MAX ||
852217dcce0SSteven Grimm             (limit_to_mtu && c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) {
853c9607c6dSBrad Fitzpatrick             add_msghdr(c);
854c9607c6dSBrad Fitzpatrick             m = &c->msglist[c->msgused - 1];
855c9607c6dSBrad Fitzpatrick         }
85686969ea4SBrad Fitzpatrick 
85777dde9f9SPaul Lindner         if (ensure_iov_space(c) != 0)
858c9607c6dSBrad Fitzpatrick             return -1;
85986969ea4SBrad Fitzpatrick 
860c9607c6dSBrad Fitzpatrick         /* If the fragment is too big to fit in the datagram, split it up */
861c9607c6dSBrad Fitzpatrick         if (limit_to_mtu && len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) {
862c9607c6dSBrad Fitzpatrick             leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE;
863c9607c6dSBrad Fitzpatrick             len -= leftover;
864c9607c6dSBrad Fitzpatrick         } else {
865c9607c6dSBrad Fitzpatrick             leftover = 0;
866c9607c6dSBrad Fitzpatrick         }
86786969ea4SBrad Fitzpatrick 
868c9607c6dSBrad Fitzpatrick         m = &c->msglist[c->msgused - 1];
86933328ee2SBrad Fitzpatrick         m->msg_iov[m->msg_iovlen].iov_base = (void *)buf;
870c9607c6dSBrad Fitzpatrick         m->msg_iov[m->msg_iovlen].iov_len = len;
87186969ea4SBrad Fitzpatrick 
872c9607c6dSBrad Fitzpatrick         c->msgbytes += len;
873c9607c6dSBrad Fitzpatrick         c->iovused++;
874c9607c6dSBrad Fitzpatrick         m->msg_iovlen++;
87586969ea4SBrad Fitzpatrick 
876c9607c6dSBrad Fitzpatrick         buf = ((char *)buf) + len;
877c9607c6dSBrad Fitzpatrick         len = leftover;
878c9607c6dSBrad Fitzpatrick     } while (leftover > 0);
87986969ea4SBrad Fitzpatrick 
880c9607c6dSBrad Fitzpatrick     return 0;
881c9607c6dSBrad Fitzpatrick }
88286969ea4SBrad Fitzpatrick 
add_chunked_item_iovs(conn * c,item * it,int len)883b05653f9Sdormando static int add_chunked_item_iovs(conn *c, item *it, int len) {
884b05653f9Sdormando     assert(it->it_flags & ITEM_CHUNKED);
885b05653f9Sdormando     item_chunk *ch = (item_chunk *) ITEM_data(it);
886b05653f9Sdormando     while (ch) {
887b05653f9Sdormando         int todo = (len > ch->used) ? ch->used : len;
888b05653f9Sdormando         if (add_iov(c, ch->data, todo) != 0) {
889b05653f9Sdormando             return -1;
890b05653f9Sdormando         }
891b05653f9Sdormando         ch = ch->next;
892b05653f9Sdormando         len -= todo;
893b05653f9Sdormando     }
894b05653f9Sdormando     return 0;
895b05653f9Sdormando }
89686969ea4SBrad Fitzpatrick 
897c9607c6dSBrad Fitzpatrick /*
898c9607c6dSBrad Fitzpatrick  * Constructs a set of UDP headers and attaches them to the outgoing messages.
899c9607c6dSBrad Fitzpatrick  */
build_udp_headers(conn * c)90077dde9f9SPaul Lindner static int build_udp_headers(conn *c) {
901c9607c6dSBrad Fitzpatrick     int i;
902c9607c6dSBrad Fitzpatrick     unsigned char *hdr;
90386969ea4SBrad Fitzpatrick 
90478955139STim Yardley     assert(c != NULL);
90578955139STim Yardley 
906c9607c6dSBrad Fitzpatrick     if (c->msgused > c->hdrsize) {
907c9607c6dSBrad Fitzpatrick         void *new_hdrbuf;
908de021a9cSTrond Norbye         if (c->hdrbuf) {
909c9607c6dSBrad Fitzpatrick             new_hdrbuf = realloc(c->hdrbuf, c->msgused * 2 * UDP_HEADER_SIZE);
910de021a9cSTrond Norbye         } else {
911c9607c6dSBrad Fitzpatrick             new_hdrbuf = malloc(c->msgused * 2 * UDP_HEADER_SIZE);
912de021a9cSTrond Norbye         }
913de021a9cSTrond Norbye 
914de021a9cSTrond Norbye         if (! new_hdrbuf) {
915de021a9cSTrond Norbye             STATS_LOCK();
916de021a9cSTrond Norbye             stats.malloc_fails++;
917de021a9cSTrond Norbye             STATS_UNLOCK();
918c9607c6dSBrad Fitzpatrick             return -1;
919de021a9cSTrond Norbye         }
920c9607c6dSBrad Fitzpatrick         c->hdrbuf = (unsigned char *)new_hdrbuf;
921c9607c6dSBrad Fitzpatrick         c->hdrsize = c->msgused * 2;
922c9607c6dSBrad Fitzpatrick     }
92386969ea4SBrad Fitzpatrick 
924c9607c6dSBrad Fitzpatrick     hdr = c->hdrbuf;
925c9607c6dSBrad Fitzpatrick     for (i = 0; i < c->msgused; i++) {
926df1b7e42STrond Norbye         c->msglist[i].msg_iov[0].iov_base = (void*)hdr;
927c9607c6dSBrad Fitzpatrick         c->msglist[i].msg_iov[0].iov_len = UDP_HEADER_SIZE;
928c9607c6dSBrad Fitzpatrick         *hdr++ = c->request_id / 256;
929c9607c6dSBrad Fitzpatrick         *hdr++ = c->request_id % 256;
930c9607c6dSBrad Fitzpatrick         *hdr++ = i / 256;
931c9607c6dSBrad Fitzpatrick         *hdr++ = i % 256;
932c9607c6dSBrad Fitzpatrick         *hdr++ = c->msgused / 256;
933c9607c6dSBrad Fitzpatrick         *hdr++ = c->msgused % 256;
934c9607c6dSBrad Fitzpatrick         *hdr++ = 0;
935c9607c6dSBrad Fitzpatrick         *hdr++ = 0;
936df1b7e42STrond Norbye         assert((void *) hdr == (caddr_t)c->msglist[i].msg_iov[0].iov_base + UDP_HEADER_SIZE);
937c9607c6dSBrad Fitzpatrick     }
93886969ea4SBrad Fitzpatrick 
939c9607c6dSBrad Fitzpatrick     return 0;
940c9607c6dSBrad Fitzpatrick }
94186969ea4SBrad Fitzpatrick 
94286969ea4SBrad Fitzpatrick 
out_string(conn * c,const char * str)94377dde9f9SPaul Lindner static void out_string(conn *c, const char *str) {
944c0ec7b09SPaolo Borelli     size_t len;
94586969ea4SBrad Fitzpatrick 
94678955139STim Yardley     assert(c != NULL);
94778955139STim Yardley 
948d9ece780STomash Brechko     if (c->noreply) {
949efbae518Sdormando         if (settings.verbose > 1)
950efbae518Sdormando             fprintf(stderr, ">%d NOREPLY %s\n", c->sfd, str);
951d9ece780STomash Brechko         c->noreply = false;
952f1351f9bSTrond Norbye         conn_set_state(c, conn_new_cmd);
953d9ece780STomash Brechko         return;
954d9ece780STomash Brechko     }
955d9ece780STomash Brechko 
9569aa4cab2SEvan Martin     if (settings.verbose > 1)
9579aa4cab2SEvan Martin         fprintf(stderr, ">%d %s\n", c->sfd, str);
95886969ea4SBrad Fitzpatrick 
9598ef1f787STrond Norbye     /* Nuke a partial output... */
9608ef1f787STrond Norbye     c->msgcurr = 0;
9618ef1f787STrond Norbye     c->msgused = 0;
9628ef1f787STrond Norbye     c->iovused = 0;
9638ef1f787STrond Norbye     add_msghdr(c);
9648ef1f787STrond Norbye 
96532f382b6SBrad Fitzpatrick     len = strlen(str);
96678955139STim Yardley     if ((len + 2) > c->wsize) {
96732f382b6SBrad Fitzpatrick         /* ought to be always enough. just fail for simplicity */
96832f382b6SBrad Fitzpatrick         str = "SERVER_ERROR output line too long";
96932f382b6SBrad Fitzpatrick         len = strlen(str);
97032f382b6SBrad Fitzpatrick     }
97186969ea4SBrad Fitzpatrick 
972c0ec7b09SPaolo Borelli     memcpy(c->wbuf, str, len);
97349f3b0caSCosimo Streppone     memcpy(c->wbuf + len, "\r\n", 2);
97432f382b6SBrad Fitzpatrick     c->wbytes = len + 2;
97532f382b6SBrad Fitzpatrick     c->wcurr = c->wbuf;
97686969ea4SBrad Fitzpatrick 
977c9607c6dSBrad Fitzpatrick     conn_set_state(c, conn_write);
978f1351f9bSTrond Norbye     c->write_and_go = conn_new_cmd;
97932f382b6SBrad Fitzpatrick     return;
98032f382b6SBrad Fitzpatrick }
98186969ea4SBrad Fitzpatrick 
98232f382b6SBrad Fitzpatrick /*
983d7757c3eSSteven Grimm  * Outputs a protocol-specific "out of memory" error. For ASCII clients,
984d7757c3eSSteven Grimm  * this is equivalent to out_string().
985d7757c3eSSteven Grimm  */
out_of_memory(conn * c,char * ascii_error)986d7757c3eSSteven Grimm static void out_of_memory(conn *c, char *ascii_error) {
987e60caafaSSteven Grimm     const static char error_prefix[] = "SERVER_ERROR ";
988e60caafaSSteven Grimm     const static int error_prefix_len = sizeof(error_prefix) - 1;
989e60caafaSSteven Grimm 
990d7757c3eSSteven Grimm     if (c->protocol == binary_prot) {
991e60caafaSSteven Grimm         /* Strip off the generic error prefix; it's irrelevant in binary */
992e60caafaSSteven Grimm         if (!strncmp(ascii_error, error_prefix, error_prefix_len)) {
993e60caafaSSteven Grimm             ascii_error += error_prefix_len;
994e60caafaSSteven Grimm         }
995e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, ascii_error, 0);
996d7757c3eSSteven Grimm     } else {
997d7757c3eSSteven Grimm         out_string(c, ascii_error);
998d7757c3eSSteven Grimm     }
999d7757c3eSSteven Grimm }
1000d7757c3eSSteven Grimm 
1001d7757c3eSSteven Grimm /*
100232f382b6SBrad Fitzpatrick  * we get here after reading the value in set/add/replace commands. The command
10030a77fdfaSDustin Sallings  * has been stored in c->cmd, and the item is ready in c->item.
100432f382b6SBrad Fitzpatrick  */
complete_nread_ascii(conn * c)10056aafe58eSDustin Sallings static void complete_nread_ascii(conn *c) {
100678955139STim Yardley     assert(c != NULL);
100778955139STim Yardley 
100832f382b6SBrad Fitzpatrick     item *it = c->item;
10090a77fdfaSDustin Sallings     int comm = c->cmd;
1010e5d053c3SDustin Sallings     enum store_item_type ret;
10110567967aSdormando     bool is_valid = false;
101286969ea4SBrad Fitzpatrick 
10131fdfb7e9STrond Norbye     pthread_mutex_lock(&c->thread->stats.mutex);
10149bce42f2Sdormando     c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++;
10151fdfb7e9STrond Norbye     pthread_mutex_unlock(&c->thread->stats.mutex);
10166a4869acSBrad Fitzpatrick 
10170567967aSdormando     if ((it->it_flags & ITEM_CHUNKED) == 0) {
1018b05653f9Sdormando         if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) == 0) {
10190567967aSdormando             is_valid = true;
10200567967aSdormando         }
10210567967aSdormando     } else {
10220567967aSdormando         char buf[2];
10230567967aSdormando         /* should point to the final item chunk */
10240567967aSdormando         item_chunk *ch = (item_chunk *) c->ritem;
10250567967aSdormando         assert(ch->used != 0);
10260567967aSdormando         /* :( We need to look at the last two bytes. This could span two
10270567967aSdormando          * chunks.
10280567967aSdormando          */
10290567967aSdormando         if (ch->used > 1) {
10300567967aSdormando             buf[0] = ch->data[ch->used - 2];
10310567967aSdormando             buf[1] = ch->data[ch->used - 1];
10320567967aSdormando         } else {
10330567967aSdormando             assert(ch->prev);
10340567967aSdormando             assert(ch->used == 1);
10350567967aSdormando             buf[0] = ch->prev->data[ch->prev->used - 1];
10360567967aSdormando             buf[1] = ch->data[ch->used - 1];
10370567967aSdormando         }
10380567967aSdormando         if (strncmp(buf, "\r\n", 2) == 0) {
10390567967aSdormando             is_valid = true;
10400567967aSdormando         } else {
10410567967aSdormando             assert(1 == 0);
10420567967aSdormando         }
10430567967aSdormando     }
10440567967aSdormando 
10450567967aSdormando     if (!is_valid) {
104632f382b6SBrad Fitzpatrick         out_string(c, "CLIENT_ERROR bad data chunk");
104756b8339eSSteven Grimm     } else {
1048a85a6e15STrond Norbye       ret = store_item(it, comm, c);
104980ec0955STrond Norbye 
105080ec0955STrond Norbye #ifdef ENABLE_DTRACE
1051eda68b70STrond Norbye       uint64_t cas = ITEM_get_cas(it);
10520a77fdfaSDustin Sallings       switch (c->cmd) {
105368957214STrond Norbye       case NREAD_ADD:
105480ec0955STrond Norbye           MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
1055eda68b70STrond Norbye                                 (ret == 1) ? it->nbytes : -1, cas);
105668957214STrond Norbye           break;
105768957214STrond Norbye       case NREAD_REPLACE:
105880ec0955STrond Norbye           MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
1059eda68b70STrond Norbye                                     (ret == 1) ? it->nbytes : -1, cas);
106068957214STrond Norbye           break;
106168957214STrond Norbye       case NREAD_APPEND:
106280ec0955STrond Norbye           MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
1063eda68b70STrond Norbye                                    (ret == 1) ? it->nbytes : -1, cas);
106468957214STrond Norbye           break;
106568957214STrond Norbye       case NREAD_PREPEND:
106680ec0955STrond Norbye           MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
1067eda68b70STrond Norbye                                     (ret == 1) ? it->nbytes : -1, cas);
106868957214STrond Norbye           break;
106968957214STrond Norbye       case NREAD_SET:
107080ec0955STrond Norbye           MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
1071eda68b70STrond Norbye                                 (ret == 1) ? it->nbytes : -1, cas);
107268957214STrond Norbye           break;
107368957214STrond Norbye       case NREAD_CAS:
107480ec0955STrond Norbye           MEMCACHED_COMMAND_CAS(c->sfd, ITEM_key(it), it->nkey, it->nbytes,
1075eda68b70STrond Norbye                                 cas);
107668957214STrond Norbye           break;
107768957214STrond Norbye       }
107868957214STrond Norbye #endif
107980ec0955STrond Norbye 
108009a40db5SDustin Sallings       switch (ret) {
108109a40db5SDustin Sallings       case STORED:
108280ec0955STrond Norbye           out_string(c, "STORED");
108309a40db5SDustin Sallings           break;
108409a40db5SDustin Sallings       case EXISTS:
1085e4a45965SDustin Sallings           out_string(c, "EXISTS");
108609a40db5SDustin Sallings           break;
108709a40db5SDustin Sallings       case NOT_FOUND:
1088579466f2Sdormando           out_string(c, "NOT_FOUND");
108909a40db5SDustin Sallings           break;
109009a40db5SDustin Sallings       case NOT_STORED:
109156b8339eSSteven Grimm           out_string(c, "NOT_STORED");
109209a40db5SDustin Sallings           break;
109309a40db5SDustin Sallings       default:
109409a40db5SDustin Sallings           out_string(c, "SERVER_ERROR Unhandled storage type.");
109509a40db5SDustin Sallings       }
109609a40db5SDustin Sallings 
109756b8339eSSteven Grimm     }
109886969ea4SBrad Fitzpatrick 
109956b8339eSSteven Grimm     item_remove(c->item);       /* release the c->item reference */
110056b8339eSSteven Grimm     c->item = 0;
110156b8339eSSteven Grimm }
110256b8339eSSteven Grimm 
1103a85a6e15STrond Norbye /**
1104a85a6e15STrond Norbye  * get a pointer to the start of the request struct for the current command
1105a85a6e15STrond Norbye  */
binary_get_request(conn * c)1106a85a6e15STrond Norbye static void* binary_get_request(conn *c) {
1107a85a6e15STrond Norbye     char *ret = c->rcurr;
1108a85a6e15STrond Norbye     ret -= (sizeof(c->binary_header) + c->binary_header.request.keylen +
1109a85a6e15STrond Norbye             c->binary_header.request.extlen);
11105268604aSTrond Norbye 
11115268604aSTrond Norbye     assert(ret >= c->rbuf);
1112a85a6e15STrond Norbye     return ret;
1113a85a6e15STrond Norbye }
1114a85a6e15STrond Norbye 
1115a85a6e15STrond Norbye /**
1116a85a6e15STrond Norbye  * get a pointer to the key in this request
1117a85a6e15STrond Norbye  */
binary_get_key(conn * c)1118a85a6e15STrond Norbye static char* binary_get_key(conn *c) {
1119a85a6e15STrond Norbye     return c->rcurr - (c->binary_header.request.keylen);
1120a85a6e15STrond Norbye }
1121a85a6e15STrond Norbye 
add_bin_header(conn * c,uint16_t err,uint8_t hdr_len,uint16_t key_len,uint32_t body_len)1122a85a6e15STrond Norbye static void add_bin_header(conn *c, uint16_t err, uint8_t hdr_len, uint16_t key_len, uint32_t body_len) {
1123a85a6e15STrond Norbye     protocol_binary_response_header* header;
11246aafe58eSDustin Sallings 
11256aafe58eSDustin Sallings     assert(c);
11266aafe58eSDustin Sallings 
11276aafe58eSDustin Sallings     c->msgcurr = 0;
11286aafe58eSDustin Sallings     c->msgused = 0;
11296aafe58eSDustin Sallings     c->iovused = 0;
11306aafe58eSDustin Sallings     if (add_msghdr(c) != 0) {
1131d7757c3eSSteven Grimm         /* This should never run out of memory because iov and msg lists
1132d7757c3eSSteven Grimm          * have minimum sizes big enough to hold an error response.
1133d7757c3eSSteven Grimm          */
1134d7757c3eSSteven Grimm         out_of_memory(c, "SERVER_ERROR out of memory adding binary header");
11356aafe58eSDustin Sallings         return;
11366aafe58eSDustin Sallings     }
11376aafe58eSDustin Sallings 
1138a85a6e15STrond Norbye     header = (protocol_binary_response_header *)c->wbuf;
113974b3a805SDustin Sallings 
1140a85a6e15STrond Norbye     header->response.magic = (uint8_t)PROTOCOL_BINARY_RES;
11410e8a58a8STrond Norbye     header->response.opcode = c->binary_header.request.opcode;
1142a85a6e15STrond Norbye     header->response.keylen = (uint16_t)htons(key_len);
11436aafe58eSDustin Sallings 
1144a85a6e15STrond Norbye     header->response.extlen = (uint8_t)hdr_len;
1145a85a6e15STrond Norbye     header->response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES;
1146a85a6e15STrond Norbye     header->response.status = (uint16_t)htons(err);
1147a85a6e15STrond Norbye 
1148a85a6e15STrond Norbye     header->response.bodylen = htonl(body_len);
1149a85a6e15STrond Norbye     header->response.opaque = c->opaque;
11509791b779STrond Norbye     header->response.cas = htonll(c->cas);
11516aafe58eSDustin Sallings 
11526aafe58eSDustin Sallings     if (settings.verbose > 1) {
1153a85a6e15STrond Norbye         int ii;
1154a85a6e15STrond Norbye         fprintf(stderr, ">%d Writing bin response:", c->sfd);
1155a85a6e15STrond Norbye         for (ii = 0; ii < sizeof(header->bytes); ++ii) {
1156a85a6e15STrond Norbye             if (ii % 4 == 0) {
1157a85a6e15STrond Norbye                 fprintf(stderr, "\n>%d  ", c->sfd);
1158a85a6e15STrond Norbye             }
1159a85a6e15STrond Norbye             fprintf(stderr, " 0x%02x", header->bytes[ii]);
1160a85a6e15STrond Norbye         }
1161a85a6e15STrond Norbye         fprintf(stderr, "\n");
11626aafe58eSDustin Sallings     }
11636aafe58eSDustin Sallings 
1164a85a6e15STrond Norbye     add_iov(c, c->wbuf, sizeof(header->response));
11656aafe58eSDustin Sallings }
11666aafe58eSDustin Sallings 
1167e60caafaSSteven Grimm /**
1168e60caafaSSteven Grimm  * Writes a binary error response. If errstr is supplied, it is used as the
1169e60caafaSSteven Grimm  * error text; otherwise a generic description of the error status code is
1170e60caafaSSteven Grimm  * included.
1171e60caafaSSteven Grimm  */
write_bin_error(conn * c,protocol_binary_response_status err,const char * errstr,int swallow)1172e60caafaSSteven Grimm static void write_bin_error(conn *c, protocol_binary_response_status err,
1173e60caafaSSteven Grimm                             const char *errstr, int swallow) {
1174a85a6e15STrond Norbye     size_t len;
1175a85a6e15STrond Norbye 
1176e60caafaSSteven Grimm     if (!errstr) {
11776aafe58eSDustin Sallings         switch (err) {
1178a85a6e15STrond Norbye         case PROTOCOL_BINARY_RESPONSE_ENOMEM:
1179f1351f9bSTrond Norbye             errstr = "Out of memory";
1180f1351f9bSTrond Norbye             break;
1181a85a6e15STrond Norbye         case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
11826aafe58eSDustin Sallings             errstr = "Unknown command";
11836aafe58eSDustin Sallings             break;
1184a85a6e15STrond Norbye         case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
11856aafe58eSDustin Sallings             errstr = "Not found";
11866aafe58eSDustin Sallings             break;
1187a85a6e15STrond Norbye         case PROTOCOL_BINARY_RESPONSE_EINVAL:
11886aafe58eSDustin Sallings             errstr = "Invalid arguments";
11896aafe58eSDustin Sallings             break;
1190a85a6e15STrond Norbye         case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
11916aafe58eSDustin Sallings             errstr = "Data exists for key.";
11926aafe58eSDustin Sallings             break;
1193a85a6e15STrond Norbye         case PROTOCOL_BINARY_RESPONSE_E2BIG:
11946aafe58eSDustin Sallings             errstr = "Too large.";
11956aafe58eSDustin Sallings             break;
1196cce46e8fSDustin Sallings         case PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL:
1197cce46e8fSDustin Sallings             errstr = "Non-numeric server-side value for incr or decr";
1198cce46e8fSDustin Sallings             break;
1199a85a6e15STrond Norbye         case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
12006aafe58eSDustin Sallings             errstr = "Not stored.";
12016aafe58eSDustin Sallings             break;
1202f1307c4dSDustin Sallings         case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
1203f1307c4dSDustin Sallings             errstr = "Auth failure.";
1204f1307c4dSDustin Sallings             break;
12056aafe58eSDustin Sallings         default:
1206a85a6e15STrond Norbye             assert(false);
12076aafe58eSDustin Sallings             errstr = "UNHANDLED ERROR";
1208a85a6e15STrond Norbye             fprintf(stderr, ">%d UNHANDLED ERROR: %d\n", c->sfd, err);
12096aafe58eSDustin Sallings         }
1210e60caafaSSteven Grimm     }
12116aafe58eSDustin Sallings 
1212783da601Sdormando     if (settings.verbose > 1) {
1213a85a6e15STrond Norbye         fprintf(stderr, ">%d Writing an error: %s\n", c->sfd, errstr);
1214a85a6e15STrond Norbye     }
1215a85a6e15STrond Norbye 
1216a85a6e15STrond Norbye     len = strlen(errstr);
1217a85a6e15STrond Norbye     add_bin_header(c, err, 0, 0, len);
1218a85a6e15STrond Norbye     if (len > 0) {
1219a85a6e15STrond Norbye         add_iov(c, errstr, len);
1220a85a6e15STrond Norbye     }
12216aafe58eSDustin Sallings     conn_set_state(c, conn_mwrite);
12226aafe58eSDustin Sallings     if(swallow > 0) {
12236aafe58eSDustin Sallings         c->sbytes = swallow;
12246aafe58eSDustin Sallings         c->write_and_go = conn_swallow;
12256aafe58eSDustin Sallings     } else {
1226f1351f9bSTrond Norbye         c->write_and_go = conn_new_cmd;
12276aafe58eSDustin Sallings     }
12286aafe58eSDustin Sallings }
12296aafe58eSDustin Sallings 
12306aafe58eSDustin Sallings /* Form and send a response to a command over the binary protocol */
write_bin_response(conn * c,void * d,int hlen,int keylen,int dlen)1231a85a6e15STrond Norbye static void write_bin_response(conn *c, void *d, int hlen, int keylen, int dlen) {
12320e8a58a8STrond Norbye     if (!c->noreply || c->cmd == PROTOCOL_BINARY_CMD_GET ||
12330e8a58a8STrond Norbye         c->cmd == PROTOCOL_BINARY_CMD_GETK) {
1234a85a6e15STrond Norbye         add_bin_header(c, 0, hlen, keylen, dlen);
12356aafe58eSDustin Sallings         if(dlen > 0) {
12366aafe58eSDustin Sallings             add_iov(c, d, dlen);
12376aafe58eSDustin Sallings         }
12386aafe58eSDustin Sallings         conn_set_state(c, conn_mwrite);
1239f1351f9bSTrond Norbye         c->write_and_go = conn_new_cmd;
12400e8a58a8STrond Norbye     } else {
12410e8a58a8STrond Norbye         conn_set_state(c, conn_new_cmd);
12420e8a58a8STrond Norbye     }
12436aafe58eSDustin Sallings }
12446aafe58eSDustin Sallings 
complete_incr_bin(conn * c)12456aafe58eSDustin Sallings static void complete_incr_bin(conn *c) {
12466aafe58eSDustin Sallings     item *it;
12476aafe58eSDustin Sallings     char *key;
12486aafe58eSDustin Sallings     size_t nkey;
1249ea2d42a5Sdormando     /* Weird magic in add_delta forces me to pad here */
1250ea2d42a5Sdormando     char tmpbuf[INCR_MAX_STORAGE_LEN];
1251ea2d42a5Sdormando     uint64_t cas = 0;
1252a85a6e15STrond Norbye 
1253a85a6e15STrond Norbye     protocol_binary_response_incr* rsp = (protocol_binary_response_incr*)c->wbuf;
1254a85a6e15STrond Norbye     protocol_binary_request_incr* req = binary_get_request(c);
12556aafe58eSDustin Sallings 
12566aafe58eSDustin Sallings     assert(c != NULL);
1257a85a6e15STrond Norbye     assert(c->wsize >= sizeof(*rsp));
12586aafe58eSDustin Sallings 
1259a85a6e15STrond Norbye     /* fix byteorder in the request */
12609791b779STrond Norbye     req->message.body.delta = ntohll(req->message.body.delta);
12619791b779STrond Norbye     req->message.body.initial = ntohll(req->message.body.initial);
1262a85a6e15STrond Norbye     req->message.body.expiration = ntohl(req->message.body.expiration);
1263a85a6e15STrond Norbye     key = binary_get_key(c);
1264a85a6e15STrond Norbye     nkey = c->binary_header.request.keylen;
12656aafe58eSDustin Sallings 
1266783da601Sdormando     if (settings.verbose > 1) {
1267a85a6e15STrond Norbye         int i;
12686aafe58eSDustin Sallings         fprintf(stderr, "incr ");
1269a85a6e15STrond Norbye 
12706aafe58eSDustin Sallings         for (i = 0; i < nkey; i++) {
12716aafe58eSDustin Sallings             fprintf(stderr, "%c", key[i]);
12726aafe58eSDustin Sallings         }
127391f3ce6eSTrond Norbye         fprintf(stderr, " %lld, %llu, %d\n",
127491f3ce6eSTrond Norbye                 (long long)req->message.body.delta,
127591f3ce6eSTrond Norbye                 (long long)req->message.body.initial,
127691f3ce6eSTrond Norbye                 req->message.body.expiration);
12776aafe58eSDustin Sallings     }
12786aafe58eSDustin Sallings 
1279ea2d42a5Sdormando     if (c->binary_header.request.cas != 0) {
1280ea2d42a5Sdormando         cas = c->binary_header.request.cas;
1281d044acb2SDustin Sallings     }
1282ea2d42a5Sdormando     switch(add_delta(c, key, nkey, c->cmd == PROTOCOL_BINARY_CMD_INCREMENT,
1283ea2d42a5Sdormando                      req->message.body.delta, tmpbuf,
1284ea2d42a5Sdormando                      &cas)) {
1285ea2d42a5Sdormando     case OK:
12869791b779STrond Norbye         rsp->message.body.value = htonll(strtoull(tmpbuf, NULL, 10));
1287ea2d42a5Sdormando         if (cas) {
1288ea2d42a5Sdormando             c->cas = cas;
1289ea2d42a5Sdormando         }
1290a85a6e15STrond Norbye         write_bin_response(c, &rsp->message.body, 0, 0,
1291a85a6e15STrond Norbye                            sizeof(rsp->message.body.value));
1292ea2d42a5Sdormando         break;
1293ea2d42a5Sdormando     case NON_NUMERIC:
1294e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL, NULL, 0);
1295ea2d42a5Sdormando         break;
1296ea2d42a5Sdormando     case EOM:
1297e60caafaSSteven Grimm         out_of_memory(c, "SERVER_ERROR Out of memory incrementing value");
1298ea2d42a5Sdormando         break;
1299ea2d42a5Sdormando     case DELTA_ITEM_NOT_FOUND:
1300ea2d42a5Sdormando         if (req->message.body.expiration != 0xffffffff) {
13016aafe58eSDustin Sallings             /* Save some room for the response */
13029791b779STrond Norbye             rsp->message.body.value = htonll(req->message.body.initial);
13038818bb69Stheblop 
13048818bb69Stheblop             snprintf(tmpbuf, INCR_MAX_STORAGE_LEN, "%llu",
13058818bb69Stheblop                 (unsigned long long)req->message.body.initial);
13068818bb69Stheblop             int res = strlen(tmpbuf);
1307a85a6e15STrond Norbye             it = item_alloc(key, nkey, 0, realtime(req->message.body.expiration),
13088818bb69Stheblop                             res + 2);
13096aafe58eSDustin Sallings 
1310a85a6e15STrond Norbye             if (it != NULL) {
13118818bb69Stheblop                 memcpy(ITEM_data(it), tmpbuf, res);
13128818bb69Stheblop                 memcpy(ITEM_data(it) + res, "\r\n", 2);
1313a85a6e15STrond Norbye 
1314ea2d42a5Sdormando                 if (store_item(it, NREAD_ADD, c)) {
1315eda68b70STrond Norbye                     c->cas = ITEM_get_cas(it);
1316a85a6e15STrond Norbye                     write_bin_response(c, &rsp->message.body, 0, 0, sizeof(rsp->message.body.value));
13176aafe58eSDustin Sallings                 } else {
1318e60caafaSSteven Grimm                     write_bin_error(c, PROTOCOL_BINARY_RESPONSE_NOT_STORED,
1319e60caafaSSteven Grimm                                     NULL, 0);
13206aafe58eSDustin Sallings                 }
13216aafe58eSDustin Sallings                 item_remove(it);         /* release our reference */
13226aafe58eSDustin Sallings             } else {
1323e60caafaSSteven Grimm                 out_of_memory(c,
1324e60caafaSSteven Grimm                         "SERVER_ERROR Out of memory allocating new item");
13256aafe58eSDustin Sallings             }
1326a85a6e15STrond Norbye         } else {
13273e030782SDustin Sallings             pthread_mutex_lock(&c->thread->stats.mutex);
13283e030782SDustin Sallings             if (c->cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
13293e030782SDustin Sallings                 c->thread->stats.incr_misses++;
13303e030782SDustin Sallings             } else {
13313e030782SDustin Sallings                 c->thread->stats.decr_misses++;
13323e030782SDustin Sallings             }
13333e030782SDustin Sallings             pthread_mutex_unlock(&c->thread->stats.mutex);
13343e030782SDustin Sallings 
1335e60caafaSSteven Grimm             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
13366aafe58eSDustin Sallings         }
1337ea2d42a5Sdormando         break;
1338ea2d42a5Sdormando     case DELTA_ITEM_CAS_MISMATCH:
1339e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
1340ea2d42a5Sdormando         break;
1341ea2d42a5Sdormando     }
13426aafe58eSDustin Sallings }
13436aafe58eSDustin Sallings 
complete_update_bin(conn * c)13446aafe58eSDustin Sallings static void complete_update_bin(conn *c) {
134514e36b3fSDustin Sallings     protocol_binary_response_status eno = PROTOCOL_BINARY_RESPONSE_EINVAL;
1346e5d053c3SDustin Sallings     enum store_item_type ret = NOT_STORED;
13476aafe58eSDustin Sallings     assert(c != NULL);
13486aafe58eSDustin Sallings 
13496aafe58eSDustin Sallings     item *it = c->item;
13506aafe58eSDustin Sallings 
13511fdfb7e9STrond Norbye     pthread_mutex_lock(&c->thread->stats.mutex);
13529bce42f2Sdormando     c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++;
13531fdfb7e9STrond Norbye     pthread_mutex_unlock(&c->thread->stats.mutex);
13546aafe58eSDustin Sallings 
13556aafe58eSDustin Sallings     /* We don't actually receive the trailing two characters in the bin
13566aafe58eSDustin Sallings      * protocol, so we're going to just set them here */
1357b05653f9Sdormando     if ((it->it_flags & ITEM_CHUNKED) == 0) {
13586aafe58eSDustin Sallings         *(ITEM_data(it) + it->nbytes - 2) = '\r';
13596aafe58eSDustin Sallings         *(ITEM_data(it) + it->nbytes - 1) = '\n';
1360b05653f9Sdormando     } else {
1361b05653f9Sdormando         assert(c->ritem);
1362b05653f9Sdormando         item_chunk *ch = (item_chunk *) c->ritem;
13636975235cSdormando         if (ch->size == ch->used)
13646975235cSdormando             ch = ch->next;
13656975235cSdormando         if (ch->size - ch->used > 1) {
13666975235cSdormando             ch->data[ch->used + 1] = '\r';
13676975235cSdormando             ch->data[ch->used + 2] = '\n';
13686975235cSdormando             ch->used += 2;
1369b05653f9Sdormando         } else {
13706975235cSdormando             ch->data[ch->used + 1] = '\r';
13716975235cSdormando             ch->next->data[0] = '\n';
13726975235cSdormando             ch->used++;
13736975235cSdormando             ch->next->used++;
13746975235cSdormando             assert(ch->size == ch->used);
1375b05653f9Sdormando         }
1376b05653f9Sdormando     }
13776aafe58eSDustin Sallings 
13780a77fdfaSDustin Sallings     ret = store_item(it, c->cmd, c);
137980ec0955STrond Norbye 
138080ec0955STrond Norbye #ifdef ENABLE_DTRACE
1381eda68b70STrond Norbye     uint64_t cas = ITEM_get_cas(it);
13820a77fdfaSDustin Sallings     switch (c->cmd) {
138380ec0955STrond Norbye     case NREAD_ADD:
138480ec0955STrond Norbye         MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
1385e5d053c3SDustin Sallings                               (ret == STORED) ? it->nbytes : -1, cas);
138680ec0955STrond Norbye         break;
138780ec0955STrond Norbye     case NREAD_REPLACE:
138880ec0955STrond Norbye         MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
1389e5d053c3SDustin Sallings                                   (ret == STORED) ? it->nbytes : -1, cas);
139080ec0955STrond Norbye         break;
139180ec0955STrond Norbye     case NREAD_APPEND:
139280ec0955STrond Norbye         MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
1393e5d053c3SDustin Sallings                                  (ret == STORED) ? it->nbytes : -1, cas);
139480ec0955STrond Norbye         break;
139580ec0955STrond Norbye     case NREAD_PREPEND:
139680ec0955STrond Norbye         MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
1397e5d053c3SDustin Sallings                                  (ret == STORED) ? it->nbytes : -1, cas);
139880ec0955STrond Norbye         break;
139980ec0955STrond Norbye     case NREAD_SET:
140080ec0955STrond Norbye         MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
1401e5d053c3SDustin Sallings                               (ret == STORED) ? it->nbytes : -1, cas);
140280ec0955STrond Norbye         break;
140380ec0955STrond Norbye     }
140480ec0955STrond Norbye #endif
140580ec0955STrond Norbye 
140680ec0955STrond Norbye     switch (ret) {
1407e5d053c3SDustin Sallings     case STORED:
14086aafe58eSDustin Sallings         /* Stored */
1409a85a6e15STrond Norbye         write_bin_response(c, NULL, 0, 0, 0);
14101379edf3SDustin Sallings         break;
1411e5d053c3SDustin Sallings     case EXISTS:
1412e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
14131379edf3SDustin Sallings         break;
1414e5d053c3SDustin Sallings     case NOT_FOUND:
1415e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
14161379edf3SDustin Sallings         break;
1417a727d7beSDustin Sallings     case NOT_STORED:
1418c7fbccebSdormando     case TOO_LARGE:
1419c7fbccebSdormando     case NO_MEMORY:
14200a77fdfaSDustin Sallings         if (c->cmd == NREAD_ADD) {
1421a85a6e15STrond Norbye             eno = PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
14220a77fdfaSDustin Sallings         } else if(c->cmd == NREAD_REPLACE) {
1423a85a6e15STrond Norbye             eno = PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
14246aafe58eSDustin Sallings         } else {
1425a85a6e15STrond Norbye             eno = PROTOCOL_BINARY_RESPONSE_NOT_STORED;
14266aafe58eSDustin Sallings         }
1427e60caafaSSteven Grimm         write_bin_error(c, eno, NULL, 0);
14286aafe58eSDustin Sallings     }
14296aafe58eSDustin Sallings 
14306aafe58eSDustin Sallings     item_remove(c->item);       /* release the c->item reference */
14316aafe58eSDustin Sallings     c->item = 0;
14326aafe58eSDustin Sallings }
14336aafe58eSDustin Sallings 
process_bin_get_or_touch(conn * c)1434c7e6ff80SSteven Grimm static void process_bin_get_or_touch(conn *c) {
1435d87f568aSdormando     item *it;
1436d87f568aSdormando 
1437d87f568aSdormando     protocol_binary_response_get* rsp = (protocol_binary_response_get*)c->wbuf;
1438d87f568aSdormando     char* key = binary_get_key(c);
1439d87f568aSdormando     size_t nkey = c->binary_header.request.keylen;
1440c7e6ff80SSteven Grimm     int should_touch = (c->cmd == PROTOCOL_BINARY_CMD_TOUCH ||
1441c7e6ff80SSteven Grimm                         c->cmd == PROTOCOL_BINARY_CMD_GAT ||
1442c7e6ff80SSteven Grimm                         c->cmd == PROTOCOL_BINARY_CMD_GATK);
1443c7e6ff80SSteven Grimm     int should_return_key = (c->cmd == PROTOCOL_BINARY_CMD_GETK ||
1444c7e6ff80SSteven Grimm                              c->cmd == PROTOCOL_BINARY_CMD_GATK);
1445c7e6ff80SSteven Grimm     int should_return_value = (c->cmd != PROTOCOL_BINARY_CMD_TOUCH);
1446c7e6ff80SSteven Grimm 
1447c7e6ff80SSteven Grimm     if (settings.verbose > 1) {
1448c7e6ff80SSteven Grimm         fprintf(stderr, "<%d %s ", c->sfd, should_touch ? "TOUCH" : "GET");
1449467d8a6dSCaleb Shay         if (fwrite(key, 1, nkey, stderr)) {}
1450c7e6ff80SSteven Grimm         fputc('\n', stderr);
1451c7e6ff80SSteven Grimm     }
1452c7e6ff80SSteven Grimm 
1453c7e6ff80SSteven Grimm     if (should_touch) {
1454045da59dSMaksim Zhylinski         protocol_binary_request_touch *t = binary_get_request(c);
1455045da59dSMaksim Zhylinski         time_t exptime = ntohl(t->message.body.expiration);
1456d87f568aSdormando 
14576895d23eSsergiocarlos         it = item_touch(key, nkey, realtime(exptime), c);
1458c7e6ff80SSteven Grimm     } else {
14596895d23eSsergiocarlos         it = item_get(key, nkey, c);
1460c7e6ff80SSteven Grimm     }
1461d87f568aSdormando 
1462d87f568aSdormando     if (it) {
1463d87f568aSdormando         /* the length has two unnecessary bytes ("\r\n") */
1464d87f568aSdormando         uint16_t keylen = 0;
1465d87f568aSdormando         uint32_t bodylen = sizeof(rsp->message.body) + (it->nbytes - 2);
1466d87f568aSdormando 
1467c9055e84Sdormando         item_update(it);
1468d87f568aSdormando         pthread_mutex_lock(&c->thread->stats.mutex);
1469c7e6ff80SSteven Grimm         if (should_touch) {
1470d87f568aSdormando             c->thread->stats.touch_cmds++;
14719bce42f2Sdormando             c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
1472c7e6ff80SSteven Grimm         } else {
1473c7e6ff80SSteven Grimm             c->thread->stats.get_cmds++;
14749bce42f2Sdormando             c->thread->stats.slab_stats[ITEM_clsid(it)].get_hits++;
1475c7e6ff80SSteven Grimm         }
1476d87f568aSdormando         pthread_mutex_unlock(&c->thread->stats.mutex);
1477d87f568aSdormando 
1478c7e6ff80SSteven Grimm         if (should_touch) {
1479d87f568aSdormando             MEMCACHED_COMMAND_TOUCH(c->sfd, ITEM_key(it), it->nkey,
1480d87f568aSdormando                                     it->nbytes, ITEM_get_cas(it));
1481c7e6ff80SSteven Grimm         } else {
1482c7e6ff80SSteven Grimm             MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
1483c7e6ff80SSteven Grimm                                   it->nbytes, ITEM_get_cas(it));
1484c7e6ff80SSteven Grimm         }
1485d87f568aSdormando 
1486d87f568aSdormando         if (c->cmd == PROTOCOL_BINARY_CMD_TOUCH) {
1487d87f568aSdormando             bodylen -= it->nbytes - 2;
1488c7e6ff80SSteven Grimm         } else if (should_return_key) {
14890d16e8c0Sdormando             bodylen += nkey;
14900d16e8c0Sdormando             keylen = nkey;
1491d87f568aSdormando         }
1492d87f568aSdormando 
1493d87f568aSdormando         add_bin_header(c, 0, sizeof(rsp->message.body), keylen, bodylen);
1494d87f568aSdormando         rsp->message.header.response.cas = htonll(ITEM_get_cas(it));
1495d87f568aSdormando 
1496d87f568aSdormando         // add the flags
1497d87f568aSdormando         rsp->message.body.flags = htonl(strtoul(ITEM_suffix(it), NULL, 10));
1498d87f568aSdormando         add_iov(c, &rsp->message.body, sizeof(rsp->message.body));
1499d87f568aSdormando 
1500c7e6ff80SSteven Grimm         if (should_return_key) {
15010d16e8c0Sdormando             add_iov(c, ITEM_key(it), nkey);
15020d16e8c0Sdormando         }
15030d16e8c0Sdormando 
1504c7e6ff80SSteven Grimm         if (should_return_value) {
1505d87f568aSdormando             /* Add the data minus the CRLF */
1506b05653f9Sdormando             if ((it->it_flags & ITEM_CHUNKED) == 0) {
1507d87f568aSdormando                 add_iov(c, ITEM_data(it), it->nbytes - 2);
1508b05653f9Sdormando             } else {
1509b05653f9Sdormando                 add_chunked_item_iovs(c, it, it->nbytes - 2);
1510b05653f9Sdormando             }
1511d87f568aSdormando         }
15120d16e8c0Sdormando 
1513d87f568aSdormando         conn_set_state(c, conn_mwrite);
1514cff1f140Sdormando         c->write_and_go = conn_new_cmd;
1515d87f568aSdormando         /* Remember this command so we can garbage collect it later */
1516d87f568aSdormando         c->item = it;
1517d87f568aSdormando     } else {
1518d87f568aSdormando         pthread_mutex_lock(&c->thread->stats.mutex);
1519c7e6ff80SSteven Grimm         if (should_touch) {
1520d87f568aSdormando             c->thread->stats.touch_cmds++;
1521d87f568aSdormando             c->thread->stats.touch_misses++;
1522d87f568aSdormando         } else {
15231fdfb7e9STrond Norbye             c->thread->stats.get_cmds++;
15241fdfb7e9STrond Norbye             c->thread->stats.get_misses++;
1525c7e6ff80SSteven Grimm         }
15261fdfb7e9STrond Norbye         pthread_mutex_unlock(&c->thread->stats.mutex);
15271fdfb7e9STrond Norbye 
1528c7e6ff80SSteven Grimm         if (should_touch) {
1529c7e6ff80SSteven Grimm             MEMCACHED_COMMAND_TOUCH(c->sfd, key, nkey, -1, 0);
1530c7e6ff80SSteven Grimm         } else {
153180ec0955STrond Norbye             MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
1532c7e6ff80SSteven Grimm         }
153388269cbfSToru Maesaka 
15340e8a58a8STrond Norbye         if (c->noreply) {
1535f1351f9bSTrond Norbye             conn_set_state(c, conn_new_cmd);
15366aafe58eSDustin Sallings         } else {
1537c7e6ff80SSteven Grimm             if (should_return_key) {
1538a85a6e15STrond Norbye                 char *ofs = c->wbuf + sizeof(protocol_binary_response_header);
1539a85a6e15STrond Norbye                 add_bin_header(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
1540a85a6e15STrond Norbye                         0, nkey, nkey);
1541a85a6e15STrond Norbye                 memcpy(ofs, key, nkey);
1542a85a6e15STrond Norbye                 add_iov(c, ofs, nkey);
1543a85a6e15STrond Norbye                 conn_set_state(c, conn_mwrite);
1544d492aaf6SDaniel Pañeda                 c->write_and_go = conn_new_cmd;
1545a85a6e15STrond Norbye             } else {
1546e60caafaSSteven Grimm                 write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
1547e60caafaSSteven Grimm                                 NULL, 0);
1548a85a6e15STrond Norbye             }
15496aafe58eSDustin Sallings         }
15506aafe58eSDustin Sallings     }
155180ec0955STrond Norbye 
155280ec0955STrond Norbye     if (settings.detail_enabled) {
155380ec0955STrond Norbye         stats_prefix_record_get(key, nkey, NULL != it);
155480ec0955STrond Norbye     }
15556aafe58eSDustin Sallings }
15566aafe58eSDustin Sallings 
append_bin_stats(const char * key,const uint16_t klen,const char * val,const uint32_t vlen,conn * c)155717df5c0eSTrond Norbye static void append_bin_stats(const char *key, const uint16_t klen,
155817df5c0eSTrond Norbye                              const char *val, const uint32_t vlen,
155917df5c0eSTrond Norbye                              conn *c) {
156017df5c0eSTrond Norbye     char *buf = c->stats.buffer + c->stats.offset;
15615e40caa4STrond Norbye     uint32_t bodylen = klen + vlen;
15625e40caa4STrond Norbye     protocol_binary_response_header header = {
15635e40caa4STrond Norbye         .response.magic = (uint8_t)PROTOCOL_BINARY_RES,
15645e40caa4STrond Norbye         .response.opcode = PROTOCOL_BINARY_CMD_STAT,
15655e40caa4STrond Norbye         .response.keylen = (uint16_t)htons(klen),
15665e40caa4STrond Norbye         .response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES,
15675e40caa4STrond Norbye         .response.bodylen = htonl(bodylen),
15685e40caa4STrond Norbye         .response.opaque = c->opaque
15695e40caa4STrond Norbye     };
15704c77f591SToru Maesaka 
15715e40caa4STrond Norbye     memcpy(buf, header.bytes, sizeof(header.response));
15725e40caa4STrond Norbye     buf += sizeof(header.response);
15734c77f591SToru Maesaka 
15744c77f591SToru Maesaka     if (klen > 0) {
15755e40caa4STrond Norbye         memcpy(buf, key, klen);
15765e40caa4STrond Norbye         buf += klen;
15774c77f591SToru Maesaka 
15784c77f591SToru Maesaka         if (vlen > 0) {
15795e40caa4STrond Norbye             memcpy(buf, val, vlen);
15804c77f591SToru Maesaka         }
15814c77f591SToru Maesaka     }
1582f4a8e7ffSToru Maesaka 
158317df5c0eSTrond Norbye     c->stats.offset += sizeof(header.response) + bodylen;
158417df5c0eSTrond Norbye }
158517df5c0eSTrond Norbye 
append_ascii_stats(const char * key,const uint16_t klen,const char * val,const uint32_t vlen,conn * c)158617df5c0eSTrond Norbye static void append_ascii_stats(const char *key, const uint16_t klen,
158717df5c0eSTrond Norbye                                const char *val, const uint32_t vlen,
158817df5c0eSTrond Norbye                                conn *c) {
158917df5c0eSTrond Norbye     char *pos = c->stats.buffer + c->stats.offset;
159027bff0d1SDustin Sallings     uint32_t nbytes = 0;
159127bff0d1SDustin Sallings     int remaining = c->stats.size - c->stats.offset;
159227bff0d1SDustin Sallings     int room = remaining - 1;
159317df5c0eSTrond Norbye 
159417df5c0eSTrond Norbye     if (klen == 0 && vlen == 0) {
159527bff0d1SDustin Sallings         nbytes = snprintf(pos, room, "END\r\n");
159617df5c0eSTrond Norbye     } else if (vlen == 0) {
159727bff0d1SDustin Sallings         nbytes = snprintf(pos, room, "STAT %s\r\n", key);
159817df5c0eSTrond Norbye     } else {
159927bff0d1SDustin Sallings         nbytes = snprintf(pos, room, "STAT %s %s\r\n", key, val);
160017df5c0eSTrond Norbye     }
160117df5c0eSTrond Norbye 
160217df5c0eSTrond Norbye     c->stats.offset += nbytes;
160317df5c0eSTrond Norbye }
160417df5c0eSTrond Norbye 
grow_stats_buf(conn * c,size_t needed)160517df5c0eSTrond Norbye static bool grow_stats_buf(conn *c, size_t needed) {
16067a0de5bfSDustin Sallings     size_t nsize = c->stats.size;
16077a0de5bfSDustin Sallings     size_t available = nsize - c->stats.offset;
160817df5c0eSTrond Norbye     bool rv = true;
160917df5c0eSTrond Norbye 
1610c0614576SDustin Sallings     /* Special case: No buffer -- need to allocate fresh */
1611c0614576SDustin Sallings     if (c->stats.buffer == NULL) {
1612c0614576SDustin Sallings         nsize = 1024;
1613c0614576SDustin Sallings         available = c->stats.size = c->stats.offset = 0;
1614c0614576SDustin Sallings     }
1615c0614576SDustin Sallings 
16167a0de5bfSDustin Sallings     while (needed > available) {
16177a0de5bfSDustin Sallings         assert(nsize > 0);
161817df5c0eSTrond Norbye         nsize = nsize << 1;
16197a0de5bfSDustin Sallings         available = nsize - c->stats.offset;
162017df5c0eSTrond Norbye     }
162117df5c0eSTrond Norbye 
16227a0de5bfSDustin Sallings     if (nsize != c->stats.size) {
162317df5c0eSTrond Norbye         char *ptr = realloc(c->stats.buffer, nsize);
162417df5c0eSTrond Norbye         if (ptr) {
162517df5c0eSTrond Norbye             c->stats.buffer = ptr;
162617df5c0eSTrond Norbye             c->stats.size = nsize;
162717df5c0eSTrond Norbye         } else {
1628de021a9cSTrond Norbye             STATS_LOCK();
1629de021a9cSTrond Norbye             stats.malloc_fails++;
1630de021a9cSTrond Norbye             STATS_UNLOCK();
163117df5c0eSTrond Norbye             rv = false;
163217df5c0eSTrond Norbye         }
163317df5c0eSTrond Norbye     }
163417df5c0eSTrond Norbye 
163517df5c0eSTrond Norbye     return rv;
163617df5c0eSTrond Norbye }
163717df5c0eSTrond Norbye 
append_stats(const char * key,const uint16_t klen,const char * val,const uint32_t vlen,const void * cookie)163817df5c0eSTrond Norbye static void append_stats(const char *key, const uint16_t klen,
163917df5c0eSTrond Norbye                   const char *val, const uint32_t vlen,
164017df5c0eSTrond Norbye                   const void *cookie)
164117df5c0eSTrond Norbye {
164217df5c0eSTrond Norbye     /* value without a key is invalid */
164317df5c0eSTrond Norbye     if (klen == 0 && vlen > 0) {
164417df5c0eSTrond Norbye         return ;
164517df5c0eSTrond Norbye     }
164617df5c0eSTrond Norbye 
164717df5c0eSTrond Norbye     conn *c = (conn*)cookie;
164817df5c0eSTrond Norbye 
164917df5c0eSTrond Norbye     if (c->protocol == binary_prot) {
165017df5c0eSTrond Norbye         size_t needed = vlen + klen + sizeof(protocol_binary_response_header);
165117df5c0eSTrond Norbye         if (!grow_stats_buf(c, needed)) {
165217df5c0eSTrond Norbye             return ;
165317df5c0eSTrond Norbye         }
165417df5c0eSTrond Norbye         append_bin_stats(key, klen, val, vlen, c);
165517df5c0eSTrond Norbye     } else {
165617df5c0eSTrond Norbye         size_t needed = vlen + klen + 10; // 10 == "STAT = \r\n"
165717df5c0eSTrond Norbye         if (!grow_stats_buf(c, needed)) {
165817df5c0eSTrond Norbye             return ;
165917df5c0eSTrond Norbye         }
166017df5c0eSTrond Norbye         append_ascii_stats(key, klen, val, vlen, c);
166117df5c0eSTrond Norbye     }
1662ea4b65c9SDustin Sallings 
1663ea4b65c9SDustin Sallings     assert(c->stats.offset <= c->stats.size);
16644c77f591SToru Maesaka }
16654c77f591SToru Maesaka 
process_bin_stat(conn * c)16664c77f591SToru Maesaka static void process_bin_stat(conn *c) {
16674c77f591SToru Maesaka     char *subcommand = binary_get_key(c);
16684c77f591SToru Maesaka     size_t nkey = c->binary_header.request.keylen;
16694c77f591SToru Maesaka 
1670783da601Sdormando     if (settings.verbose > 1) {
16714c77f591SToru Maesaka         int ii;
16724c77f591SToru Maesaka         fprintf(stderr, "<%d STATS ", c->sfd);
16734c77f591SToru Maesaka         for (ii = 0; ii < nkey; ++ii) {
16744c77f591SToru Maesaka             fprintf(stderr, "%c", subcommand[ii]);
16754c77f591SToru Maesaka         }
16764c77f591SToru Maesaka         fprintf(stderr, "\n");
16774c77f591SToru Maesaka     }
16784c77f591SToru Maesaka 
1679f4a8e7ffSToru Maesaka     if (nkey == 0) {
168017df5c0eSTrond Norbye         /* request all statistics */
168117df5c0eSTrond Norbye         server_stats(&append_stats, c);
168217df5c0eSTrond Norbye         (void)get_stats(NULL, 0, &append_stats, c);
168312c26695STrond Norbye     } else if (strncmp(subcommand, "reset", 5) == 0) {
1684199216aeSToru Maesaka         stats_reset();
168552778791SDustin Sallings     } else if (strncmp(subcommand, "settings", 8) == 0) {
168617df5c0eSTrond Norbye         process_stat_settings(&append_stats, c);
16872a637c62SToru Maesaka     } else if (strncmp(subcommand, "detail", 6) == 0) {
16882a637c62SToru Maesaka         char *subcmd_pos = subcommand + 6;
16892a637c62SToru Maesaka         if (strncmp(subcmd_pos, " dump", 5) == 0) {
169017df5c0eSTrond Norbye             int len;
16912a637c62SToru Maesaka             char *dump_buf = stats_prefix_dump(&len);
16922a637c62SToru Maesaka             if (dump_buf == NULL || len <= 0) {
1693e60caafaSSteven Grimm                 out_of_memory(c, "SERVER_ERROR Out of memory generating stats");
16942a637c62SToru Maesaka                 return ;
169517df5c0eSTrond Norbye             } else {
169617df5c0eSTrond Norbye                 append_stats("detailed", strlen("detailed"), dump_buf, len, c);
16972a637c62SToru Maesaka                 free(dump_buf);
16982a637c62SToru Maesaka             }
169917df5c0eSTrond Norbye         } else if (strncmp(subcmd_pos, " on", 3) == 0) {
17002a637c62SToru Maesaka             settings.detail_enabled = 1;
17012a637c62SToru Maesaka         } else if (strncmp(subcmd_pos, " off", 4) == 0) {
17022a637c62SToru Maesaka             settings.detail_enabled = 0;
17032a637c62SToru Maesaka         } else {
1704e60caafaSSteven Grimm             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
17052a637c62SToru Maesaka             return;
17062a637c62SToru Maesaka         }
1707199216aeSToru Maesaka     } else {
170817df5c0eSTrond Norbye         if (get_stats(subcommand, nkey, &append_stats, c)) {
170917df5c0eSTrond Norbye             if (c->stats.buffer == NULL) {
1710e60caafaSSteven Grimm                 out_of_memory(c, "SERVER_ERROR Out of memory generating stats");
171117df5c0eSTrond Norbye             } else {
171217df5c0eSTrond Norbye                 write_and_free(c, c->stats.buffer, c->stats.offset);
171317df5c0eSTrond Norbye                 c->stats.buffer = NULL;
171417df5c0eSTrond Norbye             }
171517df5c0eSTrond Norbye         } else {
1716e60caafaSSteven Grimm             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
171717df5c0eSTrond Norbye         }
171817df5c0eSTrond Norbye 
171917df5c0eSTrond Norbye         return;
172017df5c0eSTrond Norbye     }
172117df5c0eSTrond Norbye 
172217df5c0eSTrond Norbye     /* Append termination package and start the transfer */
172317df5c0eSTrond Norbye     append_stats(NULL, 0, NULL, 0, c);
172417df5c0eSTrond Norbye     if (c->stats.buffer == NULL) {
1725e60caafaSSteven Grimm         out_of_memory(c, "SERVER_ERROR Out of memory preparing to send stats");
172617df5c0eSTrond Norbye     } else {
172717df5c0eSTrond Norbye         write_and_free(c, c->stats.buffer, c->stats.offset);
172817df5c0eSTrond Norbye         c->stats.buffer = NULL;
17294c77f591SToru Maesaka     }
17304c77f591SToru Maesaka }
17314c77f591SToru Maesaka 
bin_read_key(conn * c,enum bin_substates next_substate,int extra)1732d86881eaSDustin Sallings static void bin_read_key(conn *c, enum bin_substates next_substate, int extra) {
17336aafe58eSDustin Sallings     assert(c);
17346aafe58eSDustin Sallings     c->substate = next_substate;
17356aafe58eSDustin Sallings     c->rlbytes = c->keylen + extra;
17367bb82191STrond Norbye 
17377bb82191STrond Norbye     /* Ok... do we have room for the extras and the key in the input buffer? */
17387bb82191STrond Norbye     ptrdiff_t offset = c->rcurr + sizeof(protocol_binary_request_header) - c->rbuf;
17397bb82191STrond Norbye     if (c->rlbytes > c->rsize - offset) {
17407bb82191STrond Norbye         size_t nsize = c->rsize;
17417bb82191STrond Norbye         size_t size = c->rlbytes + sizeof(protocol_binary_request_header);
17427bb82191STrond Norbye 
17437bb82191STrond Norbye         while (size > nsize) {
17447bb82191STrond Norbye             nsize *= 2;
17457bb82191STrond Norbye         }
17467bb82191STrond Norbye 
17477bb82191STrond Norbye         if (nsize != c->rsize) {
1748783da601Sdormando             if (settings.verbose > 1) {
17497bb82191STrond Norbye                 fprintf(stderr, "%d: Need to grow buffer from %lu to %lu\n",
17507bb82191STrond Norbye                         c->sfd, (unsigned long)c->rsize, (unsigned long)nsize);
17517bb82191STrond Norbye             }
17527bb82191STrond Norbye             char *newm = realloc(c->rbuf, nsize);
17537bb82191STrond Norbye             if (newm == NULL) {
1754de021a9cSTrond Norbye                 STATS_LOCK();
1755de021a9cSTrond Norbye                 stats.malloc_fails++;
1756de021a9cSTrond Norbye                 STATS_UNLOCK();
17577bb82191STrond Norbye                 if (settings.verbose) {
17587bb82191STrond Norbye                     fprintf(stderr, "%d: Failed to grow buffer.. closing connection\n",
17597bb82191STrond Norbye                             c->sfd);
17607bb82191STrond Norbye                 }
17617bb82191STrond Norbye                 conn_set_state(c, conn_closing);
17627bb82191STrond Norbye                 return;
17637bb82191STrond Norbye             }
17647bb82191STrond Norbye 
176589d5126bSTrond Norbye             c->rbuf= newm;
17667bb82191STrond Norbye             /* rcurr should point to the same offset in the packet */
17677bb82191STrond Norbye             c->rcurr = c->rbuf + offset - sizeof(protocol_binary_request_header);
17687bb82191STrond Norbye             c->rsize = nsize;
17697bb82191STrond Norbye         }
17707bb82191STrond Norbye         if (c->rbuf != c->rcurr) {
17717bb82191STrond Norbye             memmove(c->rbuf, c->rcurr, c->rbytes);
17727bb82191STrond Norbye             c->rcurr = c->rbuf;
1773783da601Sdormando             if (settings.verbose > 1) {
17747bb82191STrond Norbye                 fprintf(stderr, "%d: Repack input buffer\n", c->sfd);
17757bb82191STrond Norbye             }
17767bb82191STrond Norbye         }
17777bb82191STrond Norbye     }
17787bb82191STrond Norbye 
1779a85a6e15STrond Norbye     /* preserve the header in the buffer.. */
1780a85a6e15STrond Norbye     c->ritem = c->rcurr + sizeof(protocol_binary_request_header);
17816aafe58eSDustin Sallings     conn_set_state(c, conn_nread);
17826aafe58eSDustin Sallings }
17836aafe58eSDustin Sallings 
1784fcdf315bSdormando /* Just write an error message and disconnect the client */
handle_binary_protocol_error(conn * c)1785fcdf315bSdormando static void handle_binary_protocol_error(conn *c) {
1786e60caafaSSteven Grimm     write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, 0);
1787fcdf315bSdormando     if (settings.verbose) {
1788fcdf315bSdormando         fprintf(stderr, "Protocol error (opcode %02x), close connection %d\n",
1789fcdf315bSdormando                 c->binary_header.request.opcode, c->sfd);
1790fcdf315bSdormando     }
1791fcdf315bSdormando     c->write_and_go = conn_closing;
1792fcdf315bSdormando }
1793fcdf315bSdormando 
init_sasl_conn(conn * c)1794f1307c4dSDustin Sallings static void init_sasl_conn(conn *c) {
1795f1307c4dSDustin Sallings     assert(c);
1796f1307c4dSDustin Sallings     /* should something else be returned? */
1797f1307c4dSDustin Sallings     if (!settings.sasl)
1798f1307c4dSDustin Sallings         return;
1799f1307c4dSDustin Sallings 
180087c1cf0fS伊藤洋也     c->authenticated = false;
180187c1cf0fS伊藤洋也 
1802f1307c4dSDustin Sallings     if (!c->sasl_conn) {
1803f1307c4dSDustin Sallings         int result=sasl_server_new("memcached",
1804b0a858ceSDustin Sallings                                    NULL,
1805b0a858ceSDustin Sallings                                    my_sasl_hostname[0] ? my_sasl_hostname : NULL,
1806b0a858ceSDustin Sallings                                    NULL, NULL,
1807f1307c4dSDustin Sallings                                    NULL, 0, &c->sasl_conn);
1808f1307c4dSDustin Sallings         if (result != SASL_OK) {
1809f1307c4dSDustin Sallings             if (settings.verbose) {
1810f1307c4dSDustin Sallings                 fprintf(stderr, "Failed to initialize SASL conn.\n");
1811f1307c4dSDustin Sallings             }
1812f1307c4dSDustin Sallings             c->sasl_conn = NULL;
1813f1307c4dSDustin Sallings         }
1814f1307c4dSDustin Sallings     }
1815f1307c4dSDustin Sallings }
1816f1307c4dSDustin Sallings 
bin_list_sasl_mechs(conn * c)1817f1307c4dSDustin Sallings static void bin_list_sasl_mechs(conn *c) {
1818f1307c4dSDustin Sallings     // Guard against a disabled SASL.
1819f1307c4dSDustin Sallings     if (!settings.sasl) {
1820e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
1821f1307c4dSDustin Sallings                         c->binary_header.request.bodylen
1822f1307c4dSDustin Sallings                         - c->binary_header.request.keylen);
1823f1307c4dSDustin Sallings         return;
1824f1307c4dSDustin Sallings     }
1825f1307c4dSDustin Sallings 
1826f1307c4dSDustin Sallings     init_sasl_conn(c);
1827f1307c4dSDustin Sallings     const char *result_string = NULL;
1828f1307c4dSDustin Sallings     unsigned int string_length = 0;
1829f1307c4dSDustin Sallings     int result=sasl_listmech(c->sasl_conn, NULL,
1830f1307c4dSDustin Sallings                              "",   /* What to prepend the string with */
1831f1307c4dSDustin Sallings                              " ",  /* What to separate mechanisms with */
1832f1307c4dSDustin Sallings                              "",   /* What to append to the string */
1833f1307c4dSDustin Sallings                              &result_string, &string_length,
1834f1307c4dSDustin Sallings                              NULL);
1835f1307c4dSDustin Sallings     if (result != SASL_OK) {
1836f1307c4dSDustin Sallings         /* Perhaps there's a better error for this... */
1837f1307c4dSDustin Sallings         if (settings.verbose) {
1838f1307c4dSDustin Sallings             fprintf(stderr, "Failed to list SASL mechanisms.\n");
1839f1307c4dSDustin Sallings         }
1840e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
1841f1307c4dSDustin Sallings         return;
1842f1307c4dSDustin Sallings     }
1843f1307c4dSDustin Sallings     write_bin_response(c, (char*)result_string, 0, 0, string_length);
1844f1307c4dSDustin Sallings }
1845f1307c4dSDustin Sallings 
process_bin_sasl_auth(conn * c)1846f1307c4dSDustin Sallings static void process_bin_sasl_auth(conn *c) {
1847f1307c4dSDustin Sallings     // Guard for handling disabled SASL on the server.
1848f1307c4dSDustin Sallings     if (!settings.sasl) {
1849e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
1850f1307c4dSDustin Sallings                         c->binary_header.request.bodylen
1851f1307c4dSDustin Sallings                         - c->binary_header.request.keylen);
1852f1307c4dSDustin Sallings         return;
1853f1307c4dSDustin Sallings     }
1854f1307c4dSDustin Sallings 
1855f1307c4dSDustin Sallings     assert(c->binary_header.request.extlen == 0);
1856f1307c4dSDustin Sallings 
1857f1307c4dSDustin Sallings     int nkey = c->binary_header.request.keylen;
1858f1307c4dSDustin Sallings     int vlen = c->binary_header.request.bodylen - nkey;
1859f1307c4dSDustin Sallings 
18603705435bSDustin Sallings     if (nkey > MAX_SASL_MECH_LEN) {
1861e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen);
18623705435bSDustin Sallings         c->write_and_go = conn_swallow;
18633705435bSDustin Sallings         return;
18643705435bSDustin Sallings     }
18653705435bSDustin Sallings 
1866f1307c4dSDustin Sallings     char *key = binary_get_key(c);
1867f1307c4dSDustin Sallings     assert(key);
1868f1307c4dSDustin Sallings 
1869f1307c4dSDustin Sallings     item *it = item_alloc(key, nkey, 0, 0, vlen);
1870f1307c4dSDustin Sallings 
18713e10a71dSdormando     /* Can't use a chunked item for SASL authentication. */
18723e10a71dSdormando     if (it == 0 || (it->it_flags & ITEM_CHUNKED)) {
1873e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, NULL, vlen);
1874f1307c4dSDustin Sallings         c->write_and_go = conn_swallow;
1875f1307c4dSDustin Sallings         return;
1876f1307c4dSDustin Sallings     }
1877f1307c4dSDustin Sallings 
1878f1307c4dSDustin Sallings     c->item = it;
1879f1307c4dSDustin Sallings     c->ritem = ITEM_data(it);
1880f1307c4dSDustin Sallings     c->rlbytes = vlen;
1881f1307c4dSDustin Sallings     conn_set_state(c, conn_nread);
1882f1307c4dSDustin Sallings     c->substate = bin_reading_sasl_auth_data;
1883f1307c4dSDustin Sallings }
1884f1307c4dSDustin Sallings 
process_bin_complete_sasl_auth(conn * c)1885f1307c4dSDustin Sallings static void process_bin_complete_sasl_auth(conn *c) {
1886f1307c4dSDustin Sallings     assert(settings.sasl);
1887f1307c4dSDustin Sallings     const char *out = NULL;
1888f1307c4dSDustin Sallings     unsigned int outlen = 0;
1889f1307c4dSDustin Sallings 
1890f1307c4dSDustin Sallings     assert(c->item);
1891f1307c4dSDustin Sallings     init_sasl_conn(c);
1892f1307c4dSDustin Sallings 
1893f1307c4dSDustin Sallings     int nkey = c->binary_header.request.keylen;
1894f1307c4dSDustin Sallings     int vlen = c->binary_header.request.bodylen - nkey;
1895f1307c4dSDustin Sallings 
1896f1307c4dSDustin Sallings     char mech[nkey+1];
1897f1307c4dSDustin Sallings     memcpy(mech, ITEM_key((item*)c->item), nkey);
1898f1307c4dSDustin Sallings     mech[nkey] = 0x00;
1899f1307c4dSDustin Sallings 
1900f1307c4dSDustin Sallings     if (settings.verbose)
1901f1307c4dSDustin Sallings         fprintf(stderr, "mech:  ``%s'' with %d bytes of data\n", mech, vlen);
1902f1307c4dSDustin Sallings 
1903f1307c4dSDustin Sallings     const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c->item);
1904f1307c4dSDustin Sallings 
1905f1307c4dSDustin Sallings     int result=-1;
1906f1307c4dSDustin Sallings 
1907f1307c4dSDustin Sallings     switch (c->cmd) {
1908f1307c4dSDustin Sallings     case PROTOCOL_BINARY_CMD_SASL_AUTH:
1909f1307c4dSDustin Sallings         result = sasl_server_start(c->sasl_conn, mech,
1910f1307c4dSDustin Sallings                                    challenge, vlen,
1911f1307c4dSDustin Sallings                                    &out, &outlen);
1912f1307c4dSDustin Sallings         break;
1913f1307c4dSDustin Sallings     case PROTOCOL_BINARY_CMD_SASL_STEP:
1914f1307c4dSDustin Sallings         result = sasl_server_step(c->sasl_conn,
1915f1307c4dSDustin Sallings                                   challenge, vlen,
1916f1307c4dSDustin Sallings                                   &out, &outlen);
1917f1307c4dSDustin Sallings         break;
1918f1307c4dSDustin Sallings     default:
1919f1307c4dSDustin Sallings         assert(false); /* CMD should be one of the above */
1920f1307c4dSDustin Sallings         /* This code is pretty much impossible, but makes the compiler
1921f1307c4dSDustin Sallings            happier */
1922f1307c4dSDustin Sallings         if (settings.verbose) {
1923f1307c4dSDustin Sallings             fprintf(stderr, "Unhandled command %d with challenge %s\n",
1924f1307c4dSDustin Sallings                     c->cmd, challenge);
1925f1307c4dSDustin Sallings         }
1926f1307c4dSDustin Sallings         break;
1927f1307c4dSDustin Sallings     }
1928f1307c4dSDustin Sallings 
1929f1307c4dSDustin Sallings     item_unlink(c->item);
1930f1307c4dSDustin Sallings 
1931f1307c4dSDustin Sallings     if (settings.verbose) {
1932f1307c4dSDustin Sallings         fprintf(stderr, "sasl result code:  %d\n", result);
1933f1307c4dSDustin Sallings     }
1934f1307c4dSDustin Sallings 
1935f1307c4dSDustin Sallings     switch(result) {
1936f1307c4dSDustin Sallings     case SASL_OK:
193787c1cf0fS伊藤洋也         c->authenticated = true;
1938f1307c4dSDustin Sallings         write_bin_response(c, "Authenticated", 0, 0, strlen("Authenticated"));
19395100e7afSMatt Ingenthron         pthread_mutex_lock(&c->thread->stats.mutex);
19405100e7afSMatt Ingenthron         c->thread->stats.auth_cmds++;
19415100e7afSMatt Ingenthron         pthread_mutex_unlock(&c->thread->stats.mutex);
1942f1307c4dSDustin Sallings         break;
1943f1307c4dSDustin Sallings     case SASL_CONTINUE:
1944f1307c4dSDustin Sallings         add_bin_header(c, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE, 0, 0, outlen);
1945f1307c4dSDustin Sallings         if(outlen > 0) {
1946f1307c4dSDustin Sallings             add_iov(c, out, outlen);
1947f1307c4dSDustin Sallings         }
1948f1307c4dSDustin Sallings         conn_set_state(c, conn_mwrite);
1949f1307c4dSDustin Sallings         c->write_and_go = conn_new_cmd;
1950f1307c4dSDustin Sallings         break;
1951f1307c4dSDustin Sallings     default:
1952f1307c4dSDustin Sallings         if (settings.verbose)
1953f1307c4dSDustin Sallings             fprintf(stderr, "Unknown sasl response:  %d\n", result);
1954e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
19555100e7afSMatt Ingenthron         pthread_mutex_lock(&c->thread->stats.mutex);
19565100e7afSMatt Ingenthron         c->thread->stats.auth_cmds++;
19575100e7afSMatt Ingenthron         c->thread->stats.auth_errors++;
19585100e7afSMatt Ingenthron         pthread_mutex_unlock(&c->thread->stats.mutex);
1959f1307c4dSDustin Sallings     }
1960f1307c4dSDustin Sallings }
1961f1307c4dSDustin Sallings 
authenticated(conn * c)1962f1307c4dSDustin Sallings static bool authenticated(conn *c) {
1963f1307c4dSDustin Sallings     assert(settings.sasl);
1964f1307c4dSDustin Sallings     bool rv = false;
1965f1307c4dSDustin Sallings 
1966f1307c4dSDustin Sallings     switch (c->cmd) {
1967f1307c4dSDustin Sallings     case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: /* FALLTHROUGH */
1968f1307c4dSDustin Sallings     case PROTOCOL_BINARY_CMD_SASL_AUTH:       /* FALLTHROUGH */
1969f1307c4dSDustin Sallings     case PROTOCOL_BINARY_CMD_SASL_STEP:       /* FALLTHROUGH */
1970f1307c4dSDustin Sallings     case PROTOCOL_BINARY_CMD_VERSION:         /* FALLTHROUGH */
1971f1307c4dSDustin Sallings         rv = true;
1972f1307c4dSDustin Sallings         break;
1973f1307c4dSDustin Sallings     default:
197487c1cf0fS伊藤洋也         rv = c->authenticated;
1975f1307c4dSDustin Sallings     }
1976f1307c4dSDustin Sallings 
1977f1307c4dSDustin Sallings     if (settings.verbose > 1) {
1978f1307c4dSDustin Sallings         fprintf(stderr, "authenticated() in cmd 0x%02x is %s\n",
1979f1307c4dSDustin Sallings                 c->cmd, rv ? "true" : "false");
1980f1307c4dSDustin Sallings     }
1981f1307c4dSDustin Sallings 
1982f1307c4dSDustin Sallings     return rv;
1983f1307c4dSDustin Sallings }
1984f1307c4dSDustin Sallings 
dispatch_bin_command(conn * c)19856aafe58eSDustin Sallings static void dispatch_bin_command(conn *c) {
1986a85a6e15STrond Norbye     int protocol_error = 0;
19876aafe58eSDustin Sallings 
1988a85a6e15STrond Norbye     int extlen = c->binary_header.request.extlen;
1989a85a6e15STrond Norbye     int keylen = c->binary_header.request.keylen;
1990a85a6e15STrond Norbye     uint32_t bodylen = c->binary_header.request.bodylen;
1991a85a6e15STrond Norbye 
1992f1307c4dSDustin Sallings     if (settings.sasl && !authenticated(c)) {
1993e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
1994f1307c4dSDustin Sallings         c->write_and_go = conn_closing;
1995f1307c4dSDustin Sallings         return;
1996f1307c4dSDustin Sallings     }
1997f1307c4dSDustin Sallings 
199880ec0955STrond Norbye     MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
19990e8a58a8STrond Norbye     c->noreply = true;
2000fcdf315bSdormando 
2001fcdf315bSdormando     /* binprot supports 16bit keys, but internals are still 8bit */
2002fcdf315bSdormando     if (keylen > KEY_MAX_LENGTH) {
2003fcdf315bSdormando         handle_binary_protocol_error(c);
2004fcdf315bSdormando         return;
2005fcdf315bSdormando     }
2006fcdf315bSdormando 
20070e8a58a8STrond Norbye     switch (c->cmd) {
20080e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_SETQ:
20090e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_SET;
20100e8a58a8STrond Norbye         break;
20110e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_ADDQ:
20120e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_ADD;
20130e8a58a8STrond Norbye         break;
20140e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_REPLACEQ:
20150e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_REPLACE;
20160e8a58a8STrond Norbye         break;
20170e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_DELETEQ:
20180e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_DELETE;
20190e8a58a8STrond Norbye         break;
20200e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_INCREMENTQ:
20210e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_INCREMENT;
20220e8a58a8STrond Norbye         break;
20230e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_DECREMENTQ:
20240e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_DECREMENT;
20250e8a58a8STrond Norbye         break;
20260e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_QUITQ:
20270e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_QUIT;
20280e8a58a8STrond Norbye         break;
20290e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_FLUSHQ:
20300e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_FLUSH;
20310e8a58a8STrond Norbye         break;
20320e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_APPENDQ:
20330e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_APPEND;
20340e8a58a8STrond Norbye         break;
20350e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_PREPENDQ:
20360e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_PREPEND;
20370e8a58a8STrond Norbye         break;
20380e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_GETQ:
20390e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_GET;
20400e8a58a8STrond Norbye         break;
20410e8a58a8STrond Norbye     case PROTOCOL_BINARY_CMD_GETKQ:
20420e8a58a8STrond Norbye         c->cmd = PROTOCOL_BINARY_CMD_GETK;
20430e8a58a8STrond Norbye         break;
2044d87f568aSdormando     case PROTOCOL_BINARY_CMD_GATQ:
20450d16e8c0Sdormando         c->cmd = PROTOCOL_BINARY_CMD_GAT;
20460d16e8c0Sdormando         break;
20470d16e8c0Sdormando     case PROTOCOL_BINARY_CMD_GATKQ:
20487edb1a03SOskari Saarenmaa         c->cmd = PROTOCOL_BINARY_CMD_GATK;
2049d87f568aSdormando         break;
20500e8a58a8STrond Norbye     default:
20510e8a58a8STrond Norbye         c->noreply = false;
20520e8a58a8STrond Norbye     }
205380ec0955STrond Norbye 
2054a85a6e15STrond Norbye     switch (c->cmd) {
2055a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_VERSION:
2056a85a6e15STrond Norbye             if (extlen == 0 && keylen == 0 && bodylen == 0) {
2057a85a6e15STrond Norbye                 write_bin_response(c, VERSION, 0, 0, strlen(VERSION));
2058a85a6e15STrond Norbye             } else {
2059a85a6e15STrond Norbye                 protocol_error = 1;
2060a85a6e15STrond Norbye             }
20616aafe58eSDustin Sallings             break;
2062a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_FLUSH:
2063a85a6e15STrond Norbye             if (keylen == 0 && bodylen == extlen && (extlen == 0 || extlen == 4)) {
2064a85a6e15STrond Norbye                 bin_read_key(c, bin_read_flush_exptime, extlen);
2065a85a6e15STrond Norbye             } else {
2066a85a6e15STrond Norbye                 protocol_error = 1;
2067a85a6e15STrond Norbye             }
20686aafe58eSDustin Sallings             break;
2069a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_NOOP:
2070a85a6e15STrond Norbye             if (extlen == 0 && keylen == 0 && bodylen == 0) {
2071a85a6e15STrond Norbye                 write_bin_response(c, NULL, 0, 0, 0);
2072a85a6e15STrond Norbye             } else {
2073a85a6e15STrond Norbye                 protocol_error = 1;
2074a85a6e15STrond Norbye             }
20756aafe58eSDustin Sallings             break;
2076a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_SET: /* FALLTHROUGH */
2077a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_ADD: /* FALLTHROUGH */
2078a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_REPLACE:
2079a85a6e15STrond Norbye             if (extlen == 8 && keylen != 0 && bodylen >= (keylen + 8)) {
2080a85a6e15STrond Norbye                 bin_read_key(c, bin_reading_set_header, 8);
2081a85a6e15STrond Norbye             } else {
2082a85a6e15STrond Norbye                 protocol_error = 1;
2083a85a6e15STrond Norbye             }
2084a85a6e15STrond Norbye             break;
2085a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_GETQ:  /* FALLTHROUGH */
2086a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_GET:   /* FALLTHROUGH */
2087a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_GETKQ: /* FALLTHROUGH */
2088a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_GETK:
2089a85a6e15STrond Norbye             if (extlen == 0 && bodylen == keylen && keylen > 0) {
20906aafe58eSDustin Sallings                 bin_read_key(c, bin_reading_get_key, 0);
2091a85a6e15STrond Norbye             } else {
2092a85a6e15STrond Norbye                 protocol_error = 1;
2093a85a6e15STrond Norbye             }
20946aafe58eSDustin Sallings             break;
2095a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_DELETE:
20965da8dbabSTrond Norbye             if (keylen > 0 && extlen == 0 && bodylen == keylen) {
2097a85a6e15STrond Norbye                 bin_read_key(c, bin_reading_del_header, extlen);
2098a85a6e15STrond Norbye             } else {
2099a85a6e15STrond Norbye                 protocol_error = 1;
2100a85a6e15STrond Norbye             }
21016aafe58eSDustin Sallings             break;
2102a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_INCREMENT:
2103a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_DECREMENT:
2104a85a6e15STrond Norbye             if (keylen > 0 && extlen == 20 && bodylen == (keylen + extlen)) {
2105a85a6e15STrond Norbye                 bin_read_key(c, bin_reading_incr_header, 20);
2106a85a6e15STrond Norbye             } else {
2107a85a6e15STrond Norbye                 protocol_error = 1;
2108a85a6e15STrond Norbye             }
21096aafe58eSDustin Sallings             break;
2110a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_APPEND:
2111a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_PREPEND:
2112a85a6e15STrond Norbye             if (keylen > 0 && extlen == 0) {
2113a85a6e15STrond Norbye                 bin_read_key(c, bin_reading_set_header, 0);
2114a85a6e15STrond Norbye             } else {
2115a85a6e15STrond Norbye                 protocol_error = 1;
2116a85a6e15STrond Norbye             }
2117a85a6e15STrond Norbye             break;
21184c77f591SToru Maesaka         case PROTOCOL_BINARY_CMD_STAT:
21194c77f591SToru Maesaka             if (extlen == 0) {
21204c77f591SToru Maesaka                 bin_read_key(c, bin_reading_stat, 0);
21214c77f591SToru Maesaka             } else {
21224c77f591SToru Maesaka                 protocol_error = 1;
21234c77f591SToru Maesaka             }
21244c77f591SToru Maesaka             break;
2125a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_QUIT:
2126a85a6e15STrond Norbye             if (keylen == 0 && extlen == 0 && bodylen == 0) {
2127a85a6e15STrond Norbye                 write_bin_response(c, NULL, 0, 0, 0);
212857cebbfeSTrond Norbye                 c->write_and_go = conn_closing;
2129a24b1b17STrond Norbye                 if (c->noreply) {
2130a24b1b17STrond Norbye                     conn_set_state(c, conn_closing);
2131a24b1b17STrond Norbye                 }
2132a85a6e15STrond Norbye             } else {
2133a85a6e15STrond Norbye                 protocol_error = 1;
2134a85a6e15STrond Norbye             }
213557cebbfeSTrond Norbye             break;
2136f1307c4dSDustin Sallings         case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
2137f1307c4dSDustin Sallings             if (extlen == 0 && keylen == 0 && bodylen == 0) {
2138f1307c4dSDustin Sallings                 bin_list_sasl_mechs(c);
2139f1307c4dSDustin Sallings             } else {
2140f1307c4dSDustin Sallings                 protocol_error = 1;
2141f1307c4dSDustin Sallings             }
2142f1307c4dSDustin Sallings             break;
2143f1307c4dSDustin Sallings         case PROTOCOL_BINARY_CMD_SASL_AUTH:
2144f1307c4dSDustin Sallings         case PROTOCOL_BINARY_CMD_SASL_STEP:
2145f1307c4dSDustin Sallings             if (extlen == 0 && keylen != 0) {
2146f1307c4dSDustin Sallings                 bin_read_key(c, bin_reading_sasl_auth, 0);
2147f1307c4dSDustin Sallings             } else {
2148f1307c4dSDustin Sallings                 protocol_error = 1;
2149f1307c4dSDustin Sallings             }
2150f1307c4dSDustin Sallings             break;
2151d87f568aSdormando         case PROTOCOL_BINARY_CMD_TOUCH:
2152d87f568aSdormando         case PROTOCOL_BINARY_CMD_GAT:
2153d87f568aSdormando         case PROTOCOL_BINARY_CMD_GATQ:
21540d16e8c0Sdormando         case PROTOCOL_BINARY_CMD_GATK:
21550d16e8c0Sdormando         case PROTOCOL_BINARY_CMD_GATKQ:
2156d87f568aSdormando             if (extlen == 4 && keylen != 0) {
2157d87f568aSdormando                 bin_read_key(c, bin_reading_touch_key, 4);
2158d87f568aSdormando             } else {
2159d87f568aSdormando                 protocol_error = 1;
2160d87f568aSdormando             }
2161d87f568aSdormando             break;
21626aafe58eSDustin Sallings         default:
2163e60caafaSSteven Grimm             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
2164e60caafaSSteven Grimm                             bodylen);
2165a85a6e15STrond Norbye     }
2166a85a6e15STrond Norbye 
2167fcdf315bSdormando     if (protocol_error)
2168fcdf315bSdormando         handle_binary_protocol_error(c);
21696aafe58eSDustin Sallings }
21706aafe58eSDustin Sallings 
process_bin_update(conn * c)21717cfd3438SDustin Sallings static void process_bin_update(conn *c) {
21726aafe58eSDustin Sallings     char *key;
21736aafe58eSDustin Sallings     int nkey;
21746aafe58eSDustin Sallings     int vlen;
21756aafe58eSDustin Sallings     item *it;
2176a85a6e15STrond Norbye     protocol_binary_request_set* req = binary_get_request(c);
21776aafe58eSDustin Sallings 
21786aafe58eSDustin Sallings     assert(c != NULL);
21796aafe58eSDustin Sallings 
2180a85a6e15STrond Norbye     key = binary_get_key(c);
2181a85a6e15STrond Norbye     nkey = c->binary_header.request.keylen;
21826aafe58eSDustin Sallings 
2183a85a6e15STrond Norbye     /* fix byteorder in the request */
2184a85a6e15STrond Norbye     req->message.body.flags = ntohl(req->message.body.flags);
2185a85a6e15STrond Norbye     req->message.body.expiration = ntohl(req->message.body.expiration);
2186a85a6e15STrond Norbye 
2187a85a6e15STrond Norbye     vlen = c->binary_header.request.bodylen - (nkey + c->binary_header.request.extlen);
2188a85a6e15STrond Norbye 
2189783da601Sdormando     if (settings.verbose > 1) {
2190a85a6e15STrond Norbye         int ii;
2191a85a6e15STrond Norbye         if (c->cmd == PROTOCOL_BINARY_CMD_ADD) {
2192a85a6e15STrond Norbye             fprintf(stderr, "<%d ADD ", c->sfd);
2193a85a6e15STrond Norbye         } else if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
2194a85a6e15STrond Norbye             fprintf(stderr, "<%d SET ", c->sfd);
2195a85a6e15STrond Norbye         } else {
2196a85a6e15STrond Norbye             fprintf(stderr, "<%d REPLACE ", c->sfd);
2197a85a6e15STrond Norbye         }
2198a85a6e15STrond Norbye         for (ii = 0; ii < nkey; ++ii) {
2199a85a6e15STrond Norbye             fprintf(stderr, "%c", key[ii]);
2200a85a6e15STrond Norbye         }
22011379edf3SDustin Sallings 
2202a85a6e15STrond Norbye         fprintf(stderr, " Value len is %d", vlen);
2203a85a6e15STrond Norbye         fprintf(stderr, "\n");
22041379edf3SDustin Sallings     }
22056aafe58eSDustin Sallings 
22066aafe58eSDustin Sallings     if (settings.detail_enabled) {
220746654a83STrond Norbye         stats_prefix_record_set(key, nkey);
22086aafe58eSDustin Sallings     }
22096aafe58eSDustin Sallings 
2210a85a6e15STrond Norbye     it = item_alloc(key, nkey, req->message.body.flags,
2211a85a6e15STrond Norbye             realtime(req->message.body.expiration), vlen+2);
22126aafe58eSDustin Sallings 
22136aafe58eSDustin Sallings     if (it == 0) {
2214c7fbccebSdormando         enum store_item_type status;
2215a85a6e15STrond Norbye         if (! item_size_ok(nkey, req->message.body.flags, vlen + 2)) {
2216e60caafaSSteven Grimm             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
2217c7fbccebSdormando             status = TOO_LARGE;
22186aafe58eSDustin Sallings         } else {
2219e60caafaSSteven Grimm             out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
222096d50a8cSdormando             /* This error generating method eats the swallow value. Add here. */
222196d50a8cSdormando             c->sbytes = vlen;
2222c7fbccebSdormando             status = NO_MEMORY;
22236aafe58eSDustin Sallings         }
2224c7fbccebSdormando         /* FIXME: losing c->cmd since it's translated below. refactor? */
2225c7fbccebSdormando         LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE,
2226c7fbccebSdormando                 NULL, status, 0, key, nkey);
22278ff14453SDustin Sallings 
22288ff14453SDustin Sallings         /* Avoid stale data persisting in cache because we failed alloc.
22298ff14453SDustin Sallings          * Unacceptable for SET. Anywhere else too? */
22308ff14453SDustin Sallings         if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
22316895d23eSsergiocarlos             it = item_get(key, nkey, c);
22328ff14453SDustin Sallings             if (it) {
22338ff14453SDustin Sallings                 item_unlink(it);
22348ff14453SDustin Sallings                 item_remove(it);
22358ff14453SDustin Sallings             }
22368ff14453SDustin Sallings         }
22378ff14453SDustin Sallings 
22386aafe58eSDustin Sallings         /* swallow the data line */
22396aafe58eSDustin Sallings         c->write_and_go = conn_swallow;
22406aafe58eSDustin Sallings         return;
22416aafe58eSDustin Sallings     }
22426aafe58eSDustin Sallings 
2243eda68b70STrond Norbye     ITEM_set_cas(it, c->binary_header.request.cas);
22447cfd3438SDustin Sallings 
22456aafe58eSDustin Sallings     switch (c->cmd) {
2246a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_ADD:
22470a77fdfaSDustin Sallings             c->cmd = NREAD_ADD;
22486aafe58eSDustin Sallings             break;
2249a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_SET:
22500a77fdfaSDustin Sallings             c->cmd = NREAD_SET;
22516aafe58eSDustin Sallings             break;
2252a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_REPLACE:
22530a77fdfaSDustin Sallings             c->cmd = NREAD_REPLACE;
22546aafe58eSDustin Sallings             break;
22556aafe58eSDustin Sallings         default:
22566aafe58eSDustin Sallings             assert(0);
22576aafe58eSDustin Sallings     }
22586aafe58eSDustin Sallings 
2259eda68b70STrond Norbye     if (ITEM_get_cas(it) != 0) {
22600a77fdfaSDustin Sallings         c->cmd = NREAD_CAS;
22617cfd3438SDustin Sallings     }
22627cfd3438SDustin Sallings 
22636aafe58eSDustin Sallings     c->item = it;
22646aafe58eSDustin Sallings     c->ritem = ITEM_data(it);
22656aafe58eSDustin Sallings     c->rlbytes = vlen;
22666aafe58eSDustin Sallings     conn_set_state(c, conn_nread);
22676aafe58eSDustin Sallings     c->substate = bin_read_set_value;
22686aafe58eSDustin Sallings }
22696aafe58eSDustin Sallings 
process_bin_append_prepend(conn * c)2270a85a6e15STrond Norbye static void process_bin_append_prepend(conn *c) {
22716aafe58eSDustin Sallings     char *key;
2272a85a6e15STrond Norbye     int nkey;
2273a85a6e15STrond Norbye     int vlen;
2274a85a6e15STrond Norbye     item *it;
2275a85a6e15STrond Norbye 
2276a85a6e15STrond Norbye     assert(c != NULL);
2277a85a6e15STrond Norbye 
2278a85a6e15STrond Norbye     key = binary_get_key(c);
2279a85a6e15STrond Norbye     nkey = c->binary_header.request.keylen;
2280a85a6e15STrond Norbye     vlen = c->binary_header.request.bodylen - nkey;
2281a85a6e15STrond Norbye 
2282a85a6e15STrond Norbye     if (settings.verbose > 1) {
2283a85a6e15STrond Norbye         fprintf(stderr, "Value len is %d\n", vlen);
2284a85a6e15STrond Norbye     }
2285a85a6e15STrond Norbye 
2286a85a6e15STrond Norbye     if (settings.detail_enabled) {
228746654a83STrond Norbye         stats_prefix_record_set(key, nkey);
2288a85a6e15STrond Norbye     }
2289a85a6e15STrond Norbye 
2290a85a6e15STrond Norbye     it = item_alloc(key, nkey, 0, 0, vlen+2);
2291a85a6e15STrond Norbye 
2292a85a6e15STrond Norbye     if (it == 0) {
2293a85a6e15STrond Norbye         if (! item_size_ok(nkey, 0, vlen + 2)) {
2294e60caafaSSteven Grimm             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
2295a85a6e15STrond Norbye         } else {
2296e60caafaSSteven Grimm             out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
229796d50a8cSdormando             /* OOM calls eat the swallow value. Add here. */
229896d50a8cSdormando             c->sbytes = vlen;
2299a85a6e15STrond Norbye         }
2300a85a6e15STrond Norbye         /* swallow the data line */
2301a85a6e15STrond Norbye         c->write_and_go = conn_swallow;
2302a85a6e15STrond Norbye         return;
2303a85a6e15STrond Norbye     }
2304a85a6e15STrond Norbye 
2305eda68b70STrond Norbye     ITEM_set_cas(it, c->binary_header.request.cas);
2306a85a6e15STrond Norbye 
2307a85a6e15STrond Norbye     switch (c->cmd) {
2308a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_APPEND:
23090a77fdfaSDustin Sallings             c->cmd = NREAD_APPEND;
2310a85a6e15STrond Norbye             break;
2311a85a6e15STrond Norbye         case PROTOCOL_BINARY_CMD_PREPEND:
23120a77fdfaSDustin Sallings             c->cmd = NREAD_PREPEND;
2313a85a6e15STrond Norbye             break;
2314a85a6e15STrond Norbye         default:
2315a85a6e15STrond Norbye             assert(0);
2316a85a6e15STrond Norbye     }
2317a85a6e15STrond Norbye 
2318a85a6e15STrond Norbye     c->item = it;
2319a85a6e15STrond Norbye     c->ritem = ITEM_data(it);
2320a85a6e15STrond Norbye     c->rlbytes = vlen;
2321a85a6e15STrond Norbye     conn_set_state(c, conn_nread);
2322a85a6e15STrond Norbye     c->substate = bin_read_set_value;
2323a85a6e15STrond Norbye }
2324a85a6e15STrond Norbye 
process_bin_flush(conn * c)2325a85a6e15STrond Norbye static void process_bin_flush(conn *c) {
2326a85a6e15STrond Norbye     time_t exptime = 0;
2327a85a6e15STrond Norbye     protocol_binary_request_flush* req = binary_get_request(c);
232890593dcaSdormando     rel_time_t new_oldest = 0;
2329a85a6e15STrond Norbye 
2330a2f5ca50SDaniel Pañeda     if (!settings.flush_enabled) {
2331a2f5ca50SDaniel Pañeda       // flush_all is not allowed but we log it on stats
2332e60caafaSSteven Grimm       write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
2333a2f5ca50SDaniel Pañeda       return;
2334a2f5ca50SDaniel Pañeda     }
2335a2f5ca50SDaniel Pañeda 
2336a85a6e15STrond Norbye     if (c->binary_header.request.extlen == sizeof(req->message.body)) {
2337a85a6e15STrond Norbye         exptime = ntohl(req->message.body.expiration);
2338a85a6e15STrond Norbye     }
2339a85a6e15STrond Norbye 
23407cb05a48SDustin Sallings     if (exptime > 0) {
234190593dcaSdormando         new_oldest = realtime(exptime);
2342a85a6e15STrond Norbye     } else {
234390593dcaSdormando         new_oldest = current_time;
2344a85a6e15STrond Norbye     }
234590593dcaSdormando     if (settings.use_cas) {
234690593dcaSdormando         settings.oldest_live = new_oldest - 1;
234790593dcaSdormando         if (settings.oldest_live <= current_time)
234890593dcaSdormando             settings.oldest_cas = get_cas_id();
234990593dcaSdormando     } else {
235090593dcaSdormando         settings.oldest_live = new_oldest;
235190593dcaSdormando     }
2352a85a6e15STrond Norbye 
23538c3f8d84SDustin Sallings     pthread_mutex_lock(&c->thread->stats.mutex);
2354a9dbff28SDustin Sallings     c->thread->stats.flush_cmds++;
23558c3f8d84SDustin Sallings     pthread_mutex_unlock(&c->thread->stats.mutex);
2356a9dbff28SDustin Sallings 
2357a85a6e15STrond Norbye     write_bin_response(c, NULL, 0, 0, 0);
2358a85a6e15STrond Norbye }
2359a85a6e15STrond Norbye 
process_bin_delete(conn * c)2360a85a6e15STrond Norbye static void process_bin_delete(conn *c) {
23616aafe58eSDustin Sallings     item *it;
23626aafe58eSDustin Sallings 
2363a85a6e15STrond Norbye     protocol_binary_request_delete* req = binary_get_request(c);
2364a85a6e15STrond Norbye 
2365a85a6e15STrond Norbye     char* key = binary_get_key(c);
2366a85a6e15STrond Norbye     size_t nkey = c->binary_header.request.keylen;
2367a85a6e15STrond Norbye 
23686aafe58eSDustin Sallings     assert(c != NULL);
23696aafe58eSDustin Sallings 
2370783da601Sdormando     if (settings.verbose > 1) {
23710f605245SJeremy Sowden         int ii;
23720f605245SJeremy Sowden         fprintf(stderr, "Deleting ");
23730f605245SJeremy Sowden         for (ii = 0; ii < nkey; ++ii) {
23740f605245SJeremy Sowden             fprintf(stderr, "%c", key[ii]);
23750f605245SJeremy Sowden         }
23760f605245SJeremy Sowden         fprintf(stderr, "\n");
23776aafe58eSDustin Sallings     }
23786aafe58eSDustin Sallings 
23796aafe58eSDustin Sallings     if (settings.detail_enabled) {
238046654a83STrond Norbye         stats_prefix_record_delete(key, nkey);
23816aafe58eSDustin Sallings     }
23826aafe58eSDustin Sallings 
23836895d23eSsergiocarlos     it = item_get(key, nkey, c);
23846aafe58eSDustin Sallings     if (it) {
23859791b779STrond Norbye         uint64_t cas = ntohll(req->message.header.request.cas);
2386eda68b70STrond Norbye         if (cas == 0 || cas == ITEM_get_cas(it)) {
238780ec0955STrond Norbye             MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
2388a16ce583Sdormando             pthread_mutex_lock(&c->thread->stats.mutex);
23899bce42f2Sdormando             c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
2390a16ce583Sdormando             pthread_mutex_unlock(&c->thread->stats.mutex);
23916aafe58eSDustin Sallings             item_unlink(it);
2392a85a6e15STrond Norbye             write_bin_response(c, NULL, 0, 0, 0);
23936aafe58eSDustin Sallings         } else {
2394e60caafaSSteven Grimm             write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
2395a5c6b5e3SDustin Sallings         }
23965da8dbabSTrond Norbye         item_remove(it);      /* release our reference */
2397a5c6b5e3SDustin Sallings     } else {
2398e60caafaSSteven Grimm         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
2399a16ce583Sdormando         pthread_mutex_lock(&c->thread->stats.mutex);
2400a16ce583Sdormando         c->thread->stats.delete_misses++;
2401a16ce583Sdormando         pthread_mutex_unlock(&c->thread->stats.mutex);
24026aafe58eSDustin Sallings     }
24036aafe58eSDustin Sallings }
24046aafe58eSDustin Sallings 
complete_nread_binary(conn * c)24056aafe58eSDustin Sallings static void complete_nread_binary(conn *c) {
24066aafe58eSDustin Sallings     assert(c != NULL);
2407f1351f9bSTrond Norbye     assert(c->cmd >= 0);
24086aafe58eSDustin Sallings 
24096aafe58eSDustin Sallings     switch(c->substate) {
24106aafe58eSDustin Sallings     case bin_reading_set_header:
2411a85a6e15STrond Norbye         if (c->cmd == PROTOCOL_BINARY_CMD_APPEND ||
2412a85a6e15STrond Norbye                 c->cmd == PROTOCOL_BINARY_CMD_PREPEND) {
2413a85a6e15STrond Norbye             process_bin_append_prepend(c);
2414a85a6e15STrond Norbye         } else {
24157cfd3438SDustin Sallings             process_bin_update(c);
2416a85a6e15STrond Norbye         }
24176aafe58eSDustin Sallings         break;
24186aafe58eSDustin Sallings     case bin_read_set_value:
24196aafe58eSDustin Sallings         complete_update_bin(c);
24206aafe58eSDustin Sallings         break;
24216aafe58eSDustin Sallings     case bin_reading_get_key:
2422d87f568aSdormando     case bin_reading_touch_key:
2423c7e6ff80SSteven Grimm         process_bin_get_or_touch(c);
2424d87f568aSdormando         break;
24254c77f591SToru Maesaka     case bin_reading_stat:
24264c77f591SToru Maesaka         process_bin_stat(c);
24274c77f591SToru Maesaka         break;
24286aafe58eSDustin Sallings     case bin_reading_del_header:
24296aafe58eSDustin Sallings         process_bin_delete(c);
24306aafe58eSDustin Sallings         break;
24316aafe58eSDustin Sallings     case bin_reading_incr_header:
24326aafe58eSDustin Sallings         complete_incr_bin(c);
24336aafe58eSDustin Sallings         break;
2434a85a6e15STrond Norbye     case bin_read_flush_exptime:
2435a85a6e15STrond Norbye         process_bin_flush(c);
2436a85a6e15STrond Norbye         break;
2437f1307c4dSDustin Sallings     case bin_reading_sasl_auth:
2438f1307c4dSDustin Sallings         process_bin_sasl_auth(c);
2439f1307c4dSDustin Sallings         break;
2440f1307c4dSDustin Sallings     case bin_reading_sasl_auth_data:
2441f1307c4dSDustin Sallings         process_bin_complete_sasl_auth(c);
2442f1307c4dSDustin Sallings         break;
24436aafe58eSDustin Sallings     default:
24446aafe58eSDustin Sallings         fprintf(stderr, "Not handling substate %d\n", c->substate);
24456aafe58eSDustin Sallings         assert(0);
24466aafe58eSDustin Sallings     }
24476aafe58eSDustin Sallings }
24486aafe58eSDustin Sallings 
reset_cmd_handler(conn * c)2449f1351f9bSTrond Norbye static void reset_cmd_handler(conn *c) {
2450cba691f4SDustin Sallings     c->cmd = -1;
2451cba691f4SDustin Sallings     c->substate = bin_no_state;
245272cb22b0SDustin Sallings     if(c->item != NULL) {
245372cb22b0SDustin Sallings         item_remove(c->item);
245472cb22b0SDustin Sallings         c->item = NULL;
245572cb22b0SDustin Sallings     }
2456cba691f4SDustin Sallings     conn_shrink(c);
2457f1351f9bSTrond Norbye     if (c->rbytes > 0) {
2458f1351f9bSTrond Norbye         conn_set_state(c, conn_parse_cmd);
2459f1351f9bSTrond Norbye     } else {
2460f1351f9bSTrond Norbye         conn_set_state(c, conn_waiting);
2461cba691f4SDustin Sallings     }
2462cba691f4SDustin Sallings }
2463cba691f4SDustin Sallings 
complete_nread(conn * c)24646aafe58eSDustin Sallings static void complete_nread(conn *c) {
24656aafe58eSDustin Sallings     assert(c != NULL);
246615ace4b5SEric Lambert     assert(c->protocol == ascii_prot
24679ce2e0acSEric Lambert            || c->protocol == binary_prot);
24686aafe58eSDustin Sallings 
246915ace4b5SEric Lambert     if (c->protocol == ascii_prot) {
24706aafe58eSDustin Sallings         complete_nread_ascii(c);
24716aafe58eSDustin Sallings     } else if (c->protocol == binary_prot) {
24726aafe58eSDustin Sallings         complete_nread_binary(c);
24736aafe58eSDustin Sallings     }
24746aafe58eSDustin Sallings }
24756aafe58eSDustin Sallings 
24765978cf70Sdormando /* Destination must always be chunked */
24775978cf70Sdormando /* This should be part of item.c */
_store_item_copy_chunks(item * d_it,item * s_it,const int len)24785978cf70Sdormando static void _store_item_copy_chunks(item *d_it, item *s_it, const int len) {
24795978cf70Sdormando     item_chunk *dch = (item_chunk *) ITEM_data(d_it);
24805978cf70Sdormando     /* Advance dch until we find free space */
24815978cf70Sdormando     while (dch->size == dch->used) {
24825978cf70Sdormando         dch = dch->next;
24835978cf70Sdormando     }
24845978cf70Sdormando 
24855978cf70Sdormando     if (s_it->it_flags & ITEM_CHUNKED) {
24865978cf70Sdormando         int remain = len;
24875978cf70Sdormando         item_chunk *sch = (item_chunk *) ITEM_data(s_it);
24885978cf70Sdormando         int copied = 0;
2489ee461d11Sdormando         /* Fills dch's to capacity, not straight copy sch in case data is
2490ee461d11Sdormando          * being added or removed (ie append/prepend)
2491ee461d11Sdormando          */
24925978cf70Sdormando         while (sch && dch && remain) {
24935978cf70Sdormando             assert(dch->used <= dch->size);
24945978cf70Sdormando             int todo = (dch->size - dch->used < sch->used - copied)
24955978cf70Sdormando                 ? dch->size - dch->used : sch->used - copied;
24965978cf70Sdormando             if (remain < todo)
24975978cf70Sdormando                 todo = remain;
24985978cf70Sdormando             memcpy(dch->data + dch->used, sch->data + copied, todo);
24995978cf70Sdormando             dch->used += todo;
25005978cf70Sdormando             copied += todo;
25015978cf70Sdormando             remain -= todo;
25025978cf70Sdormando             assert(dch->used <= dch->size);
25035978cf70Sdormando             if (dch->size == dch->used) {
25045978cf70Sdormando                 dch = dch->next;
25055978cf70Sdormando             }
25065978cf70Sdormando             assert(copied <= sch->used);
25075978cf70Sdormando             if (copied == sch->used) {
25085978cf70Sdormando                 copied = 0;
25095978cf70Sdormando                 sch = sch->next;
25105978cf70Sdormando             }
25115978cf70Sdormando         }
25125978cf70Sdormando         /* assert that the destination had enough space for the source */
25135978cf70Sdormando         assert(remain == 0);
25145978cf70Sdormando     } else {
25155978cf70Sdormando         int done = 0;
2516ee461d11Sdormando         /* Fill dch's via a non-chunked item. */
25175978cf70Sdormando         while (len > done && dch) {
25185978cf70Sdormando             int todo = (dch->size - dch->used < len - done)
25195978cf70Sdormando                 ? dch->size - dch->used : len - done;
25205978cf70Sdormando             assert(dch->size - dch->used != 0);
25215978cf70Sdormando             memcpy(dch->data + dch->used, ITEM_data(s_it) + done, todo);
25225978cf70Sdormando             done += todo;
25235978cf70Sdormando             dch->used += todo;
25245978cf70Sdormando             assert(dch->used <= dch->size);
25255978cf70Sdormando             if (dch->size == dch->used)
25265978cf70Sdormando                 dch = dch->next;
25275978cf70Sdormando         }
25285978cf70Sdormando         assert(len == done);
25295978cf70Sdormando     }
25305978cf70Sdormando }
25315978cf70Sdormando 
_store_item_copy_data(int comm,item * old_it,item * new_it,item * add_it)25325978cf70Sdormando static void _store_item_copy_data(int comm, item *old_it, item *new_it, item *add_it) {
25335978cf70Sdormando     if (comm == NREAD_APPEND) {
25345978cf70Sdormando         if (new_it->it_flags & ITEM_CHUNKED) {
25355978cf70Sdormando             _store_item_copy_chunks(new_it, old_it, old_it->nbytes - 2);
25365978cf70Sdormando             _store_item_copy_chunks(new_it, add_it, add_it->nbytes);
25375978cf70Sdormando         } else {
25385978cf70Sdormando             memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
25395978cf70Sdormando             memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(add_it), add_it->nbytes);
25405978cf70Sdormando         }
25415978cf70Sdormando     } else {
25425978cf70Sdormando         /* NREAD_PREPEND */
25435978cf70Sdormando         if (new_it->it_flags & ITEM_CHUNKED) {
25445978cf70Sdormando             _store_item_copy_chunks(new_it, add_it, add_it->nbytes - 2);
25455978cf70Sdormando             _store_item_copy_chunks(new_it, old_it, old_it->nbytes);
25465978cf70Sdormando         } else {
25475978cf70Sdormando             memcpy(ITEM_data(new_it), ITEM_data(add_it), add_it->nbytes);
25485978cf70Sdormando             memcpy(ITEM_data(new_it) + add_it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
25495978cf70Sdormando         }
25505978cf70Sdormando     }
25515978cf70Sdormando }
25525978cf70Sdormando 
255356b8339eSSteven Grimm /*
255456b8339eSSteven Grimm  * Stores an item in the cache according to the semantics of one of the set
255556b8339eSSteven Grimm  * commands. In threaded mode, this is protected by the cache lock.
255656b8339eSSteven Grimm  *
2557e5d053c3SDustin Sallings  * Returns the state of storage.
255856b8339eSSteven Grimm  */
do_store_item(item * it,int comm,conn * c,const uint32_t hv)2559bab9acd1Sdormando enum store_item_type do_store_item(item *it, int comm, conn *c, const uint32_t hv) {
256056b8339eSSteven Grimm     char *key = ITEM_key(it);
25616895d23eSsergiocarlos     item *old_it = do_item_get(key, it->nkey, hv, c);
2562e5d053c3SDustin Sallings     enum store_item_type stored = NOT_STORED;
256386969ea4SBrad Fitzpatrick 
25646091c6deSPaul Lindner     item *new_it = NULL;
2565181ef834Sdormando     uint32_t flags;
25666091c6deSPaul Lindner 
256777dde9f9SPaul Lindner     if (old_it != NULL && comm == NREAD_ADD) {
256856b8339eSSteven Grimm         /* add only adds a nonexistent item, but promote to head of LRU */
256956b8339eSSteven Grimm         do_item_update(old_it);
25706091c6deSPaul Lindner     } else if (!old_it && (comm == NREAD_REPLACE
25716091c6deSPaul Lindner         || comm == NREAD_APPEND || comm == NREAD_PREPEND))
25726091c6deSPaul Lindner     {
257356b8339eSSteven Grimm         /* replace only replaces an existing value; don't store */
2574e4a45965SDustin Sallings     } else if (comm == NREAD_CAS) {
2575e4a45965SDustin Sallings         /* validate cas operation */
2576e4a45965SDustin Sallings         if(old_it == NULL) {
2577e4a45965SDustin Sallings             // LRU expired
2578e5d053c3SDustin Sallings             stored = NOT_FOUND;
257915e64625SDustin Sallings             pthread_mutex_lock(&c->thread->stats.mutex);
258015e64625SDustin Sallings             c->thread->stats.cas_misses++;
258115e64625SDustin Sallings             pthread_mutex_unlock(&c->thread->stats.mutex);
2582e4a45965SDustin Sallings         }
2583eda68b70STrond Norbye         else if (ITEM_get_cas(it) == ITEM_get_cas(old_it)) {
2584e4a45965SDustin Sallings             // cas validates
258515e64625SDustin Sallings             // it and old_it may belong to different classes.
258615e64625SDustin Sallings             // I'm updating the stats for the one that's getting pushed out
258715e64625SDustin Sallings             pthread_mutex_lock(&c->thread->stats.mutex);
25889bce42f2Sdormando             c->thread->stats.slab_stats[ITEM_clsid(old_it)].cas_hits++;
258915e64625SDustin Sallings             pthread_mutex_unlock(&c->thread->stats.mutex);
259015e64625SDustin Sallings 
2591bab9acd1Sdormando             item_replace(old_it, it, hv);
2592e5d053c3SDustin Sallings             stored = STORED;
25931379edf3SDustin Sallings         } else {
259415e64625SDustin Sallings             pthread_mutex_lock(&c->thread->stats.mutex);
25959bce42f2Sdormando             c->thread->stats.slab_stats[ITEM_clsid(old_it)].cas_badval++;
259615e64625SDustin Sallings             pthread_mutex_unlock(&c->thread->stats.mutex);
259715e64625SDustin Sallings 
25981379edf3SDustin Sallings             if(settings.verbose > 1) {
25991379edf3SDustin Sallings                 fprintf(stderr, "CAS:  failure: expected %llu, got %llu\n",
2600eda68b70STrond Norbye                         (unsigned long long)ITEM_get_cas(old_it),
2601eda68b70STrond Norbye                         (unsigned long long)ITEM_get_cas(it));
2602e4a45965SDustin Sallings             }
2603e5d053c3SDustin Sallings             stored = EXISTS;
2604e4a45965SDustin Sallings         }
2605e4a45965SDustin Sallings     } else {
2606c7fbccebSdormando         int failed_alloc = 0;
26076091c6deSPaul Lindner         /*
26086091c6deSPaul Lindner          * Append - combine new and old record into single one. Here it's
26096091c6deSPaul Lindner          * atomic and thread-safe.
26106091c6deSPaul Lindner          */
26116091c6deSPaul Lindner         if (comm == NREAD_APPEND || comm == NREAD_PREPEND) {
2612a85a6e15STrond Norbye             /*
2613a85a6e15STrond Norbye              * Validate CAS
2614a85a6e15STrond Norbye              */
2615eda68b70STrond Norbye             if (ITEM_get_cas(it) != 0) {
2616a85a6e15STrond Norbye                 // CAS much be equal
2617eda68b70STrond Norbye                 if (ITEM_get_cas(it) != ITEM_get_cas(old_it)) {
2618e5d053c3SDustin Sallings                     stored = EXISTS;
2619a85a6e15STrond Norbye                 }
2620a85a6e15STrond Norbye             }
26216091c6deSPaul Lindner 
2622e5d053c3SDustin Sallings             if (stored == NOT_STORED) {
26236091c6deSPaul Lindner                 /* we have it and old_it here - alloc memory to hold both */
26246091c6deSPaul Lindner                 /* flags was already lost - so recover them from ITEM_suffix(it) */
26256091c6deSPaul Lindner 
2626181ef834Sdormando                 flags = (uint32_t) strtoul(ITEM_suffix(old_it), (char **) NULL, 10);
26276091c6deSPaul Lindner 
2628*690a9a9dSEiichi Tsukata                 new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */);
26296091c6deSPaul Lindner 
26306091c6deSPaul Lindner                 if (new_it == NULL) {
2631c7fbccebSdormando                     failed_alloc = 1;
2632c7fbccebSdormando                     stored = NOT_STORED;
2633c7fbccebSdormando                 } else {
26346091c6deSPaul Lindner                     /* copy data from it and old_it to new_it */
26355978cf70Sdormando                     _store_item_copy_data(comm, old_it, new_it, it);
26366091c6deSPaul Lindner 
26376091c6deSPaul Lindner                     it = new_it;
26386091c6deSPaul Lindner                 }
2639a85a6e15STrond Norbye             }
2640c7fbccebSdormando         }
26416091c6deSPaul Lindner 
2642c7fbccebSdormando         if (stored == NOT_STORED && failed_alloc == 0) {
264356b8339eSSteven Grimm             if (old_it != NULL)
2644bab9acd1Sdormando                 item_replace(old_it, it, hv);
264556b8339eSSteven Grimm             else
2646bab9acd1Sdormando                 do_item_link(it, hv);
264756b8339eSSteven Grimm 
2648eda68b70STrond Norbye             c->cas = ITEM_get_cas(it);
2649a85a6e15STrond Norbye 
2650e5d053c3SDustin Sallings             stored = STORED;
265120892be0SBrad Fitzpatrick         }
2652a85a6e15STrond Norbye     }
265320892be0SBrad Fitzpatrick 
26546091c6deSPaul Lindner     if (old_it != NULL)
265556b8339eSSteven Grimm         do_item_remove(old_it);         /* release our reference */
26566091c6deSPaul Lindner     if (new_it != NULL)
26576091c6deSPaul Lindner         do_item_remove(new_it);
26586091c6deSPaul Lindner 
265952f16b3eSTrond Norbye     if (stored == STORED) {
266052f16b3eSTrond Norbye         c->cas = ITEM_get_cas(it);
266152f16b3eSTrond Norbye     }
2662c7fbccebSdormando     LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE, NULL,
2663c7fbccebSdormando             stored, comm, ITEM_key(it), it->nkey);
266452f16b3eSTrond Norbye 
266556b8339eSSteven Grimm     return stored;
266632f382b6SBrad Fitzpatrick }
266786969ea4SBrad Fitzpatrick 
2668217dcce0SSteven Grimm typedef struct token_s {
2669217dcce0SSteven Grimm     char *value;
2670217dcce0SSteven Grimm     size_t length;
2671217dcce0SSteven Grimm } token_t;
267286969ea4SBrad Fitzpatrick 
2673217dcce0SSteven Grimm #define COMMAND_TOKEN 0
2674217dcce0SSteven Grimm #define SUBCOMMAND_TOKEN 1
2675217dcce0SSteven Grimm #define KEY_TOKEN 1
2676217dcce0SSteven Grimm 
2677d9ece780STomash Brechko #define MAX_TOKENS 8
2678217dcce0SSteven Grimm 
2679217dcce0SSteven Grimm /*
2680217dcce0SSteven Grimm  * Tokenize the command string by replacing whitespace with '\0' and update
2681217dcce0SSteven Grimm  * the token array tokens with pointer to start of each token and length.
2682217dcce0SSteven Grimm  * Returns total number of tokens.  The last valid token is the terminal
2683217dcce0SSteven Grimm  * token (value points to the first unprocessed character of the string and
2684217dcce0SSteven Grimm  * length zero).
2685217dcce0SSteven Grimm  *
2686217dcce0SSteven Grimm  * Usage example:
2687217dcce0SSteven Grimm  *
2688217dcce0SSteven Grimm  *  while(tokenize_command(command, ncommand, tokens, max_tokens) > 0) {
2689217dcce0SSteven Grimm  *      for(int ix = 0; tokens[ix].length != 0; ix++) {
2690217dcce0SSteven Grimm  *          ...
2691217dcce0SSteven Grimm  *      }
2692217dcce0SSteven Grimm  *      ncommand = tokens[ix].value - command;
2693217dcce0SSteven Grimm  *      command  = tokens[ix].value;
2694217dcce0SSteven Grimm  *   }
2695217dcce0SSteven Grimm  */
tokenize_command(char * command,token_t * tokens,const size_t max_tokens)269677dde9f9SPaul Lindner static size_t tokenize_command(char *command, token_t *tokens, const size_t max_tokens) {
2697ce96a208SPaolo Borelli     char *s, *e;
2698217dcce0SSteven Grimm     size_t ntokens = 0;
269967b6bb1dSdormando     size_t len = strlen(command);
270067b6bb1dSdormando     unsigned int i = 0;
2701217dcce0SSteven Grimm 
2702217dcce0SSteven Grimm     assert(command != NULL && tokens != NULL && max_tokens > 1);
2703217dcce0SSteven Grimm 
270467b6bb1dSdormando     s = e = command;
270567b6bb1dSdormando     for (i = 0; i < len; i++) {
2706ce96a208SPaolo Borelli         if (*e == ' ') {
2707ce96a208SPaolo Borelli             if (s != e) {
2708ce96a208SPaolo Borelli                 tokens[ntokens].value = s;
2709ce96a208SPaolo Borelli                 tokens[ntokens].length = e - s;
2710217dcce0SSteven Grimm                 ntokens++;
2711ce96a208SPaolo Borelli                 *e = '\0';
271267b6bb1dSdormando                 if (ntokens == max_tokens - 1) {
271367b6bb1dSdormando                     e++;
271467b6bb1dSdormando                     s = e; /* so we don't add an extra token */
271567b6bb1dSdormando                     break;
271667b6bb1dSdormando                 }
2717217dcce0SSteven Grimm             }
2718ce96a208SPaolo Borelli             s = e + 1;
2719217dcce0SSteven Grimm         }
272067b6bb1dSdormando         e++;
272167b6bb1dSdormando     }
272267b6bb1dSdormando 
2723ce96a208SPaolo Borelli     if (s != e) {
2724ce96a208SPaolo Borelli         tokens[ntokens].value = s;
2725ce96a208SPaolo Borelli         tokens[ntokens].length = e - s;
2726ce96a208SPaolo Borelli         ntokens++;
2727217dcce0SSteven Grimm     }
2728217dcce0SSteven Grimm 
2729217dcce0SSteven Grimm     /*
2730217dcce0SSteven Grimm      * If we scanned the whole string, the terminal value pointer is null,
2731217dcce0SSteven Grimm      * otherwise it is the first unprocessed character.
2732217dcce0SSteven Grimm      */
2733ce96a208SPaolo Borelli     tokens[ntokens].value =  *e == '\0' ? NULL : e;
2734217dcce0SSteven Grimm     tokens[ntokens].length = 0;
2735217dcce0SSteven Grimm     ntokens++;
2736217dcce0SSteven Grimm 
2737217dcce0SSteven Grimm     return ntokens;
2738217dcce0SSteven Grimm }
2739217dcce0SSteven Grimm 
274053e4130aSSteven Grimm /* set up a connection to write a buffer then free it, used for stats */
write_and_free(conn * c,char * buf,int bytes)274153e4130aSSteven Grimm static void write_and_free(conn *c, char *buf, int bytes) {
274253e4130aSSteven Grimm     if (buf) {
274353e4130aSSteven Grimm         c->write_and_free = buf;
274453e4130aSSteven Grimm         c->wcurr = buf;
274553e4130aSSteven Grimm         c->wbytes = bytes;
274653e4130aSSteven Grimm         conn_set_state(c, conn_write);
2747f1351f9bSTrond Norbye         c->write_and_go = conn_new_cmd;
274853e4130aSSteven Grimm     } else {
2749d7757c3eSSteven Grimm         out_of_memory(c, "SERVER_ERROR out of memory writing stats");
275053e4130aSSteven Grimm     }
275153e4130aSSteven Grimm }
275253e4130aSSteven Grimm 
set_noreply_maybe(conn * c,token_t * tokens,size_t ntokens)2753dd599c0eSDustin Sallings static inline bool set_noreply_maybe(conn *c, token_t *tokens, size_t ntokens)
2754d9ece780STomash Brechko {
2755d9ece780STomash Brechko     int noreply_index = ntokens - 2;
2756d9ece780STomash Brechko 
2757d9ece780STomash Brechko     /*
2758d9ece780STomash Brechko       NOTE: this function is not the first place where we are going to
2759d9ece780STomash Brechko       send the reply.  We could send it instead from process_command()
2760d9ece780STomash Brechko       if the request line has wrong number of tokens.  However parsing
2761d9ece780STomash Brechko       malformed line for "noreply" option is not reliable anyway, so
2762d9ece780STomash Brechko       it can't be helped.
2763d9ece780STomash Brechko     */
2764d9ece780STomash Brechko     if (tokens[noreply_index].value
2765d9ece780STomash Brechko         && strcmp(tokens[noreply_index].value, "noreply") == 0) {
2766d9ece780STomash Brechko         c->noreply = true;
2767d9ece780STomash Brechko     }
2768dd599c0eSDustin Sallings     return c->noreply;
2769d9ece780STomash Brechko }
2770d9ece780STomash Brechko 
append_stat(const char * name,ADD_STAT add_stats,conn * c,const char * fmt,...)277117df5c0eSTrond Norbye void append_stat(const char *name, ADD_STAT add_stats, conn *c,
2772dd713869SDustin Sallings                  const char *fmt, ...) {
277388a68689SDustin Sallings     char val_str[STAT_VAL_LEN];
277417df5c0eSTrond Norbye     int vlen;
2775dd713869SDustin Sallings     va_list ap;
2776dd713869SDustin Sallings 
2777dd713869SDustin Sallings     assert(name);
2778dd713869SDustin Sallings     assert(add_stats);
2779dd713869SDustin Sallings     assert(c);
2780dd713869SDustin Sallings     assert(fmt);
2781dd713869SDustin Sallings 
2782dd713869SDustin Sallings     va_start(ap, fmt);
2783dd713869SDustin Sallings     vlen = vsnprintf(val_str, sizeof(val_str) - 1, fmt, ap);
2784dd713869SDustin Sallings     va_end(ap);
2785dd713869SDustin Sallings 
278617df5c0eSTrond Norbye     add_stats(name, strlen(name), val_str, vlen, c);
2787dd713869SDustin Sallings }
2788dd713869SDustin Sallings 
process_stats_detail(conn * c,const char * command)278961f543a5SPaul Lindner inline static void process_stats_detail(conn *c, const char *command) {
279056b8339eSSteven Grimm     assert(c != NULL);
279156b8339eSSteven Grimm 
279256b8339eSSteven Grimm     if (strcmp(command, "on") == 0) {
279356b8339eSSteven Grimm         settings.detail_enabled = 1;
279456b8339eSSteven Grimm         out_string(c, "OK");
279556b8339eSSteven Grimm     }
279656b8339eSSteven Grimm     else if (strcmp(command, "off") == 0) {
279756b8339eSSteven Grimm         settings.detail_enabled = 0;
279856b8339eSSteven Grimm         out_string(c, "OK");
279956b8339eSSteven Grimm     }
280056b8339eSSteven Grimm     else if (strcmp(command, "dump") == 0) {
280156b8339eSSteven Grimm         int len;
280256b8339eSSteven Grimm         char *stats = stats_prefix_dump(&len);
280353e4130aSSteven Grimm         write_and_free(c, stats, len);
280456b8339eSSteven Grimm     }
280556b8339eSSteven Grimm     else {
280656b8339eSSteven Grimm         out_string(c, "CLIENT_ERROR usage: stats detail on|off|dump");
280756b8339eSSteven Grimm     }
280856b8339eSSteven Grimm }
280956b8339eSSteven Grimm 
28104c77f591SToru Maesaka /* return server specific stats only */
server_stats(ADD_STAT add_stats,conn * c)281117df5c0eSTrond Norbye static void server_stats(ADD_STAT add_stats, conn *c) {
28123bdfd463SToru Maesaka     pid_t pid = getpid();
28134c77f591SToru Maesaka     rel_time_t now = current_time;
281486969ea4SBrad Fitzpatrick 
28151fdfb7e9STrond Norbye     struct thread_stats thread_stats;
28161fdfb7e9STrond Norbye     threadlocal_stats_aggregate(&thread_stats);
281725b5189cSDustin Sallings     struct slab_stats slab_stats;
281825b5189cSDustin Sallings     slab_stats_aggregate(&thread_stats, &slab_stats);
28191fdfb7e9STrond Norbye 
2820d9b97d80SPaul Lindner #ifndef WIN32
2821d9b97d80SPaul Lindner     struct rusage usage;
2822fb738a56SBrad Fitzpatrick     getrusage(RUSAGE_SELF, &usage);
2823d9b97d80SPaul Lindner #endif /* !WIN32 */
282486969ea4SBrad Fitzpatrick 
282556b8339eSSteven Grimm     STATS_LOCK();
28263bdfd463SToru Maesaka 
28270f5d6dc6SDustin Sallings     APPEND_STAT("pid", "%lu", (long)pid);
2828d7324b0bSdormando     APPEND_STAT("uptime", "%u", now - ITEM_UPDATE_INTERVAL);
28290f5d6dc6SDustin Sallings     APPEND_STAT("time", "%ld", now + (long)process_started);
28300f5d6dc6SDustin Sallings     APPEND_STAT("version", "%s", VERSION);
2831b9423044STrond Norbye     APPEND_STAT("libevent", "%s", event_get_version());
28320f5d6dc6SDustin Sallings     APPEND_STAT("pointer_size", "%d", (int)(8 * sizeof(void *)));
28333bdfd463SToru Maesaka 
2834d9b97d80SPaul Lindner #ifndef WIN32
283517df5c0eSTrond Norbye     append_stat("rusage_user", add_stats, c, "%ld.%06ld",
28360f5d6dc6SDustin Sallings                 (long)usage.ru_utime.tv_sec,
28373bdfd463SToru Maesaka                 (long)usage.ru_utime.tv_usec);
283817df5c0eSTrond Norbye     append_stat("rusage_system", add_stats, c, "%ld.%06ld",
28390f5d6dc6SDustin Sallings                 (long)usage.ru_stime.tv_sec,
28403bdfd463SToru Maesaka                 (long)usage.ru_stime.tv_usec);
2841d9b97d80SPaul Lindner #endif /* !WIN32 */
28423bdfd463SToru Maesaka 
2843cb01d504Sdormando     APPEND_STAT("curr_connections", "%llu", (unsigned long long)stats_state.curr_conns - 1);
28449517c656Sdormando     APPEND_STAT("total_connections", "%llu", (unsigned long long)stats.total_conns);
2845d1f9d992Sdormando     if (settings.maxconns_fast) {
2846d1f9d992Sdormando         APPEND_STAT("rejected_connections", "%llu", (unsigned long long)stats.rejected_conns);
2847d1f9d992Sdormando     }
2848cb01d504Sdormando     APPEND_STAT("connection_structures", "%u", stats_state.conn_structs);
2849cb01d504Sdormando     APPEND_STAT("reserved_fds", "%u", stats_state.reserved_fds);
28500f5d6dc6SDustin Sallings     APPEND_STAT("cmd_get", "%llu", (unsigned long long)thread_stats.get_cmds);
28510f5d6dc6SDustin Sallings     APPEND_STAT("cmd_set", "%llu", (unsigned long long)slab_stats.set_cmds);
2852534466e0Sdormando     APPEND_STAT("cmd_flush", "%llu", (unsigned long long)thread_stats.flush_cmds);
2853d87f568aSdormando     APPEND_STAT("cmd_touch", "%llu", (unsigned long long)thread_stats.touch_cmds);
28540f5d6dc6SDustin Sallings     APPEND_STAT("get_hits", "%llu", (unsigned long long)slab_stats.get_hits);
28550f5d6dc6SDustin Sallings     APPEND_STAT("get_misses", "%llu", (unsigned long long)thread_stats.get_misses);
28566895d23eSsergiocarlos     APPEND_STAT("get_expired", "%llu", (unsigned long long)thread_stats.get_expired);
285780ce0108Sdormando     APPEND_STAT("get_flushed", "%llu", (unsigned long long)thread_stats.get_flushed);
28580f5d6dc6SDustin Sallings     APPEND_STAT("delete_misses", "%llu", (unsigned long long)thread_stats.delete_misses);
28590f5d6dc6SDustin Sallings     APPEND_STAT("delete_hits", "%llu", (unsigned long long)slab_stats.delete_hits);
28600f5d6dc6SDustin Sallings     APPEND_STAT("incr_misses", "%llu", (unsigned long long)thread_stats.incr_misses);
28610f5d6dc6SDustin Sallings     APPEND_STAT("incr_hits", "%llu", (unsigned long long)slab_stats.incr_hits);
28620f5d6dc6SDustin Sallings     APPEND_STAT("decr_misses", "%llu", (unsigned long long)thread_stats.decr_misses);
28630f5d6dc6SDustin Sallings     APPEND_STAT("decr_hits", "%llu", (unsigned long long)slab_stats.decr_hits);
28640f5d6dc6SDustin Sallings     APPEND_STAT("cas_misses", "%llu", (unsigned long long)thread_stats.cas_misses);
28650f5d6dc6SDustin Sallings     APPEND_STAT("cas_hits", "%llu", (unsigned long long)slab_stats.cas_hits);
28660f5d6dc6SDustin Sallings     APPEND_STAT("cas_badval", "%llu", (unsigned long long)slab_stats.cas_badval);
2867d87f568aSdormando     APPEND_STAT("touch_hits", "%llu", (unsigned long long)slab_stats.touch_hits);
2868d87f568aSdormando     APPEND_STAT("touch_misses", "%llu", (unsigned long long)thread_stats.touch_misses);
28695100e7afSMatt Ingenthron     APPEND_STAT("auth_cmds", "%llu", (unsigned long long)thread_stats.auth_cmds);
28705100e7afSMatt Ingenthron     APPEND_STAT("auth_errors", "%llu", (unsigned long long)thread_stats.auth_errors);
287183ba6bd9SJay Grizzard     if (settings.idle_timeout) {
287283ba6bd9SJay Grizzard         APPEND_STAT("idle_kicks", "%llu", (unsigned long long)thread_stats.idle_kicks);
287383ba6bd9SJay Grizzard     }
28740f5d6dc6SDustin Sallings     APPEND_STAT("bytes_read", "%llu", (unsigned long long)thread_stats.bytes_read);
28750f5d6dc6SDustin Sallings     APPEND_STAT("bytes_written", "%llu", (unsigned long long)thread_stats.bytes_written);
28760f5d6dc6SDustin Sallings     APPEND_STAT("limit_maxbytes", "%llu", (unsigned long long)settings.maxbytes);
2877cb01d504Sdormando     APPEND_STAT("accepting_conns", "%u", stats_state.accepting_conns);
28783d540bdbSdormando     APPEND_STAT("listen_disabled_num", "%llu", (unsigned long long)stats.listen_disabled_num);
2879a1f269eeSIan Miell     APPEND_STAT("time_in_listen_disabled_us", "%llu", stats.time_in_listen_disabled_us);
28800f5d6dc6SDustin Sallings     APPEND_STAT("threads", "%d", settings.num_threads);
2881a21f819aSTrond Norbye     APPEND_STAT("conn_yields", "%llu", (unsigned long long)thread_stats.conn_yields);
2882cb01d504Sdormando     APPEND_STAT("hash_power_level", "%u", stats_state.hash_power_level);
2883cb01d504Sdormando     APPEND_STAT("hash_bytes", "%llu", (unsigned long long)stats_state.hash_bytes);
2884cb01d504Sdormando     APPEND_STAT("hash_is_expanding", "%u", stats_state.hash_is_expanding);
288510698baeSdormando     if (settings.slab_reassign) {
2886004e2211Sdormando         APPEND_STAT("slab_reassign_rescues", "%llu", stats.slab_reassign_rescues);
2887ee461d11Sdormando         APPEND_STAT("slab_reassign_chunk_rescues", "%llu", stats.slab_reassign_chunk_rescues);
28888fa54f7eSdormando         APPEND_STAT("slab_reassign_evictions_nomem", "%llu", stats.slab_reassign_evictions_nomem);
2889b1debc4cSdormando         APPEND_STAT("slab_reassign_inline_reclaim", "%llu", stats.slab_reassign_inline_reclaim);
2890004e2211Sdormando         APPEND_STAT("slab_reassign_busy_items", "%llu", stats.slab_reassign_busy_items);
2891cb01d504Sdormando         APPEND_STAT("slab_reassign_running", "%u", stats_state.slab_reassign_running);
289210698baeSdormando         APPEND_STAT("slabs_moved", "%llu", stats.slabs_moved);
289310698baeSdormando     }
28946be2b6c0Sdormando     if (settings.lru_crawler) {
2895cb01d504Sdormando         APPEND_STAT("lru_crawler_running", "%u", stats_state.lru_crawler_running);
2896c10feb9eSdormando         APPEND_STAT("lru_crawler_starts", "%u", stats.lru_crawler_starts);
28976be2b6c0Sdormando     }
2898a0390847Sdormando     if (settings.lru_maintainer_thread) {
2899a0390847Sdormando         APPEND_STAT("lru_maintainer_juggles", "%llu", (unsigned long long)stats.lru_maintainer_juggles);
2900a0390847Sdormando     }
2901de021a9cSTrond Norbye     APPEND_STAT("malloc_fails", "%llu",
2902de021a9cSTrond Norbye                 (unsigned long long)stats.malloc_fails);
29030503b5e2Sdormando     APPEND_STAT("log_worker_dropped", "%llu", (unsigned long long)stats.log_worker_dropped);
29040503b5e2Sdormando     APPEND_STAT("log_worker_written", "%llu", (unsigned long long)stats.log_worker_written);
29050503b5e2Sdormando     APPEND_STAT("log_watcher_skipped", "%llu", (unsigned long long)stats.log_watcher_skipped);
29060503b5e2Sdormando     APPEND_STAT("log_watcher_sent", "%llu", (unsigned long long)stats.log_watcher_sent);
290756b8339eSSteven Grimm     STATS_UNLOCK();
2908f4a8e7ffSToru Maesaka }
2909f4a8e7ffSToru Maesaka 
process_stat_settings(ADD_STAT add_stats,void * c)291017df5c0eSTrond Norbye static void process_stat_settings(ADD_STAT add_stats, void *c) {
291152778791SDustin Sallings     assert(add_stats);
29127068e52aSdormando     APPEND_STAT("maxbytes", "%llu", (unsigned long long)settings.maxbytes);
29130e779f76SDustin Sallings     APPEND_STAT("maxconns", "%d", settings.maxconns);
29140e779f76SDustin Sallings     APPEND_STAT("tcpport", "%d", settings.port);
29150e779f76SDustin Sallings     APPEND_STAT("udpport", "%d", settings.udpport);
29160e779f76SDustin Sallings     APPEND_STAT("inter", "%s", settings.inter ? settings.inter : "NULL");
29170e779f76SDustin Sallings     APPEND_STAT("verbosity", "%d", settings.verbose);
29180e779f76SDustin Sallings     APPEND_STAT("oldest", "%lu", (unsigned long)settings.oldest_live);
29190e779f76SDustin Sallings     APPEND_STAT("evictions", "%s", settings.evict_to_free ? "on" : "off");
29200e779f76SDustin Sallings     APPEND_STAT("domain_socket", "%s",
292152778791SDustin Sallings                 settings.socketpath ? settings.socketpath : "NULL");
29220e779f76SDustin Sallings     APPEND_STAT("umask", "%o", settings.access);
29230e779f76SDustin Sallings     APPEND_STAT("growth_factor", "%.2f", settings.factor);
29240e779f76SDustin Sallings     APPEND_STAT("chunk_size", "%d", settings.chunk_size);
29250e779f76SDustin Sallings     APPEND_STAT("num_threads", "%d", settings.num_threads);
2926c60ca35bSTrond Norbye     APPEND_STAT("num_threads_per_udp", "%d", settings.num_threads_per_udp);
29270e779f76SDustin Sallings     APPEND_STAT("stat_key_prefix", "%c", settings.prefix_delimiter);
29280e779f76SDustin Sallings     APPEND_STAT("detail_enabled", "%s",
292952778791SDustin Sallings                 settings.detail_enabled ? "yes" : "no");
29300e779f76SDustin Sallings     APPEND_STAT("reqs_per_event", "%d", settings.reqs_per_event);
29310e779f76SDustin Sallings     APPEND_STAT("cas_enabled", "%s", settings.use_cas ? "yes" : "no");
29320e779f76SDustin Sallings     APPEND_STAT("tcp_backlog", "%d", settings.backlog);
2933a155b044SDustin Sallings     APPEND_STAT("binding_protocol", "%s",
2934a155b044SDustin Sallings                 prot_text(settings.binding_protocol));
29351efcd03dSMatt Ingenthron     APPEND_STAT("auth_enabled_sasl", "%s", settings.sasl ? "yes" : "no");
2936bed5f9bbSdormando     APPEND_STAT("item_size_max", "%d", settings.item_size_max);
2937d1f9d992Sdormando     APPEND_STAT("maxconns_fast", "%s", settings.maxconns_fast ? "yes" : "no");
29381db1de38Sdormando     APPEND_STAT("hashpower_init", "%d", settings.hashpower_init);
293910698baeSdormando     APPEND_STAT("slab_reassign", "%s", settings.slab_reassign ? "yes" : "no");
294063bf748aSdormando     APPEND_STAT("slab_automove", "%d", settings.slab_automove);
29416be2b6c0Sdormando     APPEND_STAT("lru_crawler", "%s", settings.lru_crawler ? "yes" : "no");
294231d533f8Sdormando     APPEND_STAT("lru_crawler_sleep", "%d", settings.lru_crawler_sleep);
2943e31a5912Sdormando     APPEND_STAT("lru_crawler_tocrawl", "%lu", (unsigned long)settings.lru_crawler_tocrawl);
2944058af0d8SKeyur     APPEND_STAT("tail_repair_time", "%d", settings.tail_repair_time);
2945a2f5ca50SDaniel Pañeda     APPEND_STAT("flush_enabled", "%s", settings.flush_enabled ? "yes" : "no");
294605ca809cSdormando     APPEND_STAT("hash_algorithm", "%s", settings.hash_algorithm);
29478d6bf78aSdormando     APPEND_STAT("lru_maintainer_thread", "%s", settings.lru_maintainer_thread ? "yes" : "no");
29488d6bf78aSdormando     APPEND_STAT("hot_lru_pct", "%d", settings.hot_lru_pct);
2949561581d7Swangkang-xy     APPEND_STAT("warm_lru_pct", "%d", settings.warm_lru_pct);
29504de89c8cSdormando     APPEND_STAT("expirezero_does_not_evict", "%s", settings.expirezero_does_not_evict ? "yes" : "no");
295183ba6bd9SJay Grizzard     APPEND_STAT("idle_timeout", "%d", settings.idle_timeout);
2952d704f2c0Sdormando     APPEND_STAT("watcher_logbuf_size", "%u", settings.logger_watcher_buf_size);
2953d704f2c0Sdormando     APPEND_STAT("worker_logbuf_size", "%u", settings.logger_buf_size);
29548d82383fSdormando     APPEND_STAT("track_sizes", "%s", item_stats_sizes_status() ? "yes" : "no");
295552778791SDustin Sallings }
295652778791SDustin Sallings 
conn_to_str(const conn * c,char * buf)2957ae7fb537SSteven Grimm static void conn_to_str(const conn *c, char *buf) {
295870c1b5f6SSteven Grimm     char addr_text[MAXPATHLEN];
2959ae7fb537SSteven Grimm 
2960ae7fb537SSteven Grimm     if (!c) {
2961ae7fb537SSteven Grimm         strcpy(buf, "<null>");
2962ae7fb537SSteven Grimm     } else if (c->state == conn_closed) {
2963ae7fb537SSteven Grimm         strcpy(buf, "<closed>");
2964ae7fb537SSteven Grimm     } else {
2965ae7fb537SSteven Grimm         const char *protoname = "?";
296670c1b5f6SSteven Grimm         struct sockaddr_in6 local_addr;
2967ae7fb537SSteven Grimm         struct sockaddr *addr = (void *)&c->request_addr;
296870c1b5f6SSteven Grimm         int af;
296970c1b5f6SSteven Grimm         unsigned short port = 0;
297070c1b5f6SSteven Grimm 
297170c1b5f6SSteven Grimm         /* For listen ports and idle UDP ports, show listen address */
2972ae7fb537SSteven Grimm         if (c->state == conn_listening ||
2973ae7fb537SSteven Grimm                 (IS_UDP(c->transport) &&
2974ae7fb537SSteven Grimm                  c->state == conn_read)) {
297570c1b5f6SSteven Grimm             socklen_t local_addr_len = sizeof(local_addr);
297670c1b5f6SSteven Grimm 
2977ae7fb537SSteven Grimm             if (getsockname(c->sfd,
297870c1b5f6SSteven Grimm                         (struct sockaddr *)&local_addr,
297970c1b5f6SSteven Grimm                         &local_addr_len) == 0) {
298070c1b5f6SSteven Grimm                 addr = (struct sockaddr *)&local_addr;
298170c1b5f6SSteven Grimm             }
298270c1b5f6SSteven Grimm         }
298370c1b5f6SSteven Grimm 
298470c1b5f6SSteven Grimm         af = addr->sa_family;
298570c1b5f6SSteven Grimm         addr_text[0] = '\0';
298670c1b5f6SSteven Grimm 
298770c1b5f6SSteven Grimm         switch (af) {
298870c1b5f6SSteven Grimm             case AF_INET:
2989ae7fb537SSteven Grimm                 (void) inet_ntop(af,
299070c1b5f6SSteven Grimm                         &((struct sockaddr_in *)addr)->sin_addr,
299170c1b5f6SSteven Grimm                         addr_text,
299270c1b5f6SSteven Grimm                         sizeof(addr_text) - 1);
299370c1b5f6SSteven Grimm                 port = ntohs(((struct sockaddr_in *)addr)->sin_port);
2994ae7fb537SSteven Grimm                 protoname = IS_UDP(c->transport) ? "udp" : "tcp";
299570c1b5f6SSteven Grimm                 break;
299670c1b5f6SSteven Grimm 
299770c1b5f6SSteven Grimm             case AF_INET6:
299870c1b5f6SSteven Grimm                 addr_text[0] = '[';
299970c1b5f6SSteven Grimm                 addr_text[1] = '\0';
3000ae7fb537SSteven Grimm                 if (inet_ntop(af,
300170c1b5f6SSteven Grimm                         &((struct sockaddr_in6 *)addr)->sin6_addr,
300270c1b5f6SSteven Grimm                         addr_text + 1,
3003ae7fb537SSteven Grimm                         sizeof(addr_text) - 2)) {
300470c1b5f6SSteven Grimm                     strcat(addr_text, "]");
300570c1b5f6SSteven Grimm                 }
300670c1b5f6SSteven Grimm                 port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
3007ae7fb537SSteven Grimm                 protoname = IS_UDP(c->transport) ? "udp6" : "tcp6";
300870c1b5f6SSteven Grimm                 break;
300970c1b5f6SSteven Grimm 
301070c1b5f6SSteven Grimm             case AF_UNIX:
301170c1b5f6SSteven Grimm                 strncpy(addr_text,
301270c1b5f6SSteven Grimm                         ((struct sockaddr_un *)addr)->sun_path,
301370c1b5f6SSteven Grimm                         sizeof(addr_text) - 1);
301470c1b5f6SSteven Grimm                 addr_text[sizeof(addr_text)-1] = '\0';
3015ae7fb537SSteven Grimm                 protoname = "unix";
301670c1b5f6SSteven Grimm                 break;
301770c1b5f6SSteven Grimm         }
301870c1b5f6SSteven Grimm 
301970c1b5f6SSteven Grimm         if (strlen(addr_text) < 2) {
302070c1b5f6SSteven Grimm             /* Most likely this is a connected UNIX-domain client which
302170c1b5f6SSteven Grimm              * has no peer socket address, but there's no portable way
302270c1b5f6SSteven Grimm              * to tell for sure.
302370c1b5f6SSteven Grimm              */
302470c1b5f6SSteven Grimm             sprintf(addr_text, "<AF %d>", af);
302570c1b5f6SSteven Grimm         }
302670c1b5f6SSteven Grimm 
302770c1b5f6SSteven Grimm         if (port) {
3028ae7fb537SSteven Grimm             sprintf(buf, "%s:%s:%u", protoname, addr_text, port);
3029ae7fb537SSteven Grimm         } else {
3030ae7fb537SSteven Grimm             sprintf(buf, "%s:%s", protoname, addr_text);
3031ae7fb537SSteven Grimm         }
3032ae7fb537SSteven Grimm     }
303370c1b5f6SSteven Grimm }
303470c1b5f6SSteven Grimm 
process_stats_conns(ADD_STAT add_stats,void * c)3035ae7fb537SSteven Grimm static void process_stats_conns(ADD_STAT add_stats, void *c) {
3036ae7fb537SSteven Grimm     int i;
3037ae7fb537SSteven Grimm     char key_str[STAT_KEY_LEN];
3038ae7fb537SSteven Grimm     char val_str[STAT_VAL_LEN];
3039ae7fb537SSteven Grimm     char conn_name[MAXPATHLEN + sizeof("unix:")];
3040ae7fb537SSteven Grimm     int klen = 0, vlen = 0;
3041ae7fb537SSteven Grimm 
3042ae7fb537SSteven Grimm     assert(add_stats);
3043ae7fb537SSteven Grimm 
3044ae7fb537SSteven Grimm     for (i = 0; i < max_fds; i++) {
3045ae7fb537SSteven Grimm         if (conns[i]) {
3046ae7fb537SSteven Grimm             /* This is safe to do unlocked because conns are never freed; the
3047ae7fb537SSteven Grimm              * worst that'll happen will be a minor inconsistency in the
3048ae7fb537SSteven Grimm              * output -- not worth the complexity of the locking that'd be
3049ae7fb537SSteven Grimm              * required to prevent it.
3050ae7fb537SSteven Grimm              */
3051ae7fb537SSteven Grimm             if (conns[i]->state != conn_closed) {
3052ae7fb537SSteven Grimm                 conn_to_str(conns[i], conn_name);
3053ae7fb537SSteven Grimm 
3054ae7fb537SSteven Grimm                 APPEND_NUM_STAT(i, "addr", "%s", conn_name);
305570c1b5f6SSteven Grimm                 APPEND_NUM_STAT(i, "state", "%s",
305670c1b5f6SSteven Grimm                         state_text(conns[i]->state));
305770c1b5f6SSteven Grimm                 APPEND_NUM_STAT(i, "secs_since_last_cmd", "%d",
305870c1b5f6SSteven Grimm                         current_time - conns[i]->last_cmd_time);
305970c1b5f6SSteven Grimm             }
306070c1b5f6SSteven Grimm         }
306170c1b5f6SSteven Grimm     }
3062ae7fb537SSteven Grimm }
306370c1b5f6SSteven Grimm 
process_stat(conn * c,token_t * tokens,const size_t ntokens)30644c77f591SToru Maesaka static void process_stat(conn *c, token_t *tokens, const size_t ntokens) {
306517df5c0eSTrond Norbye     const char *subcommand = tokens[SUBCOMMAND_TOKEN].value;
30664c77f591SToru Maesaka     assert(c != NULL);
30674c77f591SToru Maesaka 
30684c77f591SToru Maesaka     if (ntokens < 2) {
30694c77f591SToru Maesaka         out_string(c, "CLIENT_ERROR bad command line");
30704c77f591SToru Maesaka         return;
30714c77f591SToru Maesaka     }
30724c77f591SToru Maesaka 
307317df5c0eSTrond Norbye     if (ntokens == 2) {
307417df5c0eSTrond Norbye         server_stats(&append_stats, c);
307517df5c0eSTrond Norbye         (void)get_stats(NULL, 0, &append_stats, c);
307617df5c0eSTrond Norbye     } else if (strcmp(subcommand, "reset") == 0) {
307732f382b6SBrad Fitzpatrick         stats_reset();
307832f382b6SBrad Fitzpatrick         out_string(c, "RESET");
307960bff10bSBrad Fitzpatrick         return ;
308017df5c0eSTrond Norbye     } else if (strcmp(subcommand, "detail") == 0) {
30816c6fb973SToru Maesaka         /* NOTE: how to tackle detail with binary? */
30826c6fb973SToru Maesaka         if (ntokens < 4)
30836c6fb973SToru Maesaka             process_stats_detail(c, "");  /* outputs the error message */
30846c6fb973SToru Maesaka         else
30856c6fb973SToru Maesaka             process_stats_detail(c, tokens[2].value);
308617df5c0eSTrond Norbye         /* Output already generated */
30876c6fb973SToru Maesaka         return ;
308817df5c0eSTrond Norbye     } else if (strcmp(subcommand, "settings") == 0) {
308917df5c0eSTrond Norbye         process_stat_settings(&append_stats, c);
309017df5c0eSTrond Norbye     } else if (strcmp(subcommand, "cachedump") == 0) {
309160d70942SAnatoly Vorobey         char *buf;
309260d70942SAnatoly Vorobey         unsigned int bytes, id, limit = 0;
3093217dcce0SSteven Grimm 
3094217dcce0SSteven Grimm         if (ntokens < 5) {
309560d70942SAnatoly Vorobey             out_string(c, "CLIENT_ERROR bad command line");
309660d70942SAnatoly Vorobey             return;
309760d70942SAnatoly Vorobey         }
309886969ea4SBrad Fitzpatrick 
309917df5c0eSTrond Norbye         if (!safe_strtoul(tokens[2].value, &id) ||
310017df5c0eSTrond Norbye             !safe_strtoul(tokens[3].value, &limit)) {
3101217dcce0SSteven Grimm             out_string(c, "CLIENT_ERROR bad command line format");
3102217dcce0SSteven Grimm             return;
3103217dcce0SSteven Grimm         }
3104217dcce0SSteven Grimm 
31057979cdbdSDavid Oliveira         if (id >= MAX_NUMBER_OF_SLAB_CLASSES) {
31062c7bfeb8STrond Norbye             out_string(c, "CLIENT_ERROR Illegal slab id");
31072c7bfeb8STrond Norbye             return;
31082c7bfeb8STrond Norbye         }
31092c7bfeb8STrond Norbye 
311060d70942SAnatoly Vorobey         buf = item_cachedump(id, limit, &bytes);
311153e4130aSSteven Grimm         write_and_free(c, buf, bytes);
311260d70942SAnatoly Vorobey         return ;
311370c1b5f6SSteven Grimm     } else if (strcmp(subcommand, "conns") == 0) {
311470c1b5f6SSteven Grimm         process_stats_conns(&append_stats, c);
311517df5c0eSTrond Norbye     } else {
31166c6fb973SToru Maesaka         /* getting here means that the subcommand is either engine specific or
31176c6fb973SToru Maesaka            is invalid. query the engine and see. */
311817df5c0eSTrond Norbye         if (get_stats(subcommand, strlen(subcommand), &append_stats, c)) {
311917df5c0eSTrond Norbye             if (c->stats.buffer == NULL) {
3120d7757c3eSSteven Grimm                 out_of_memory(c, "SERVER_ERROR out of memory writing stats");
312117df5c0eSTrond Norbye             } else {
312217df5c0eSTrond Norbye                 write_and_free(c, c->stats.buffer, c->stats.offset);
312317df5c0eSTrond Norbye                 c->stats.buffer = NULL;
312417df5c0eSTrond Norbye             }
312517df5c0eSTrond Norbye         } else {
312617df5c0eSTrond Norbye             out_string(c, "ERROR");
312717df5c0eSTrond Norbye         }
31286d74e134SBrad Fitzpatrick         return ;
31296d74e134SBrad Fitzpatrick     }
313086969ea4SBrad Fitzpatrick 
313117df5c0eSTrond Norbye     /* append terminator and start the transfer */
313217df5c0eSTrond Norbye     append_stats(NULL, 0, NULL, 0, c);
313317df5c0eSTrond Norbye 
313417df5c0eSTrond Norbye     if (c->stats.buffer == NULL) {
3135d7757c3eSSteven Grimm         out_of_memory(c, "SERVER_ERROR out of memory writing stats");
313617df5c0eSTrond Norbye     } else {
313717df5c0eSTrond Norbye         write_and_free(c, c->stats.buffer, c->stats.offset);
313817df5c0eSTrond Norbye         c->stats.buffer = NULL;
313917df5c0eSTrond Norbye     }
314032f382b6SBrad Fitzpatrick }
314186969ea4SBrad Fitzpatrick 
314277dde9f9SPaul Lindner /* ntokens is overwritten here... shrug.. */
process_get_command(conn * c,token_t * tokens,size_t ntokens,bool return_cas)3143e61c0a86Sdormando static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {
3144217dcce0SSteven Grimm     char *key;
3145217dcce0SSteven Grimm     size_t nkey;
3146943f2e2bSTrond Norbye     int i = 0;
3147217dcce0SSteven Grimm     item *it;
3148217dcce0SSteven Grimm     token_t *key_token = &tokens[KEY_TOKEN];
3149e61c0a86Sdormando     char *suffix;
315078955139STim Yardley     assert(c != NULL);
315178955139STim Yardley 
3152217dcce0SSteven Grimm     do {
3153217dcce0SSteven Grimm         while(key_token->length != 0) {
315486969ea4SBrad Fitzpatrick 
3155217dcce0SSteven Grimm             key = key_token->value;
3156217dcce0SSteven Grimm             nkey = key_token->length;
315786969ea4SBrad Fitzpatrick 
3158217dcce0SSteven Grimm             if(nkey > KEY_MAX_LENGTH) {
315932f382b6SBrad Fitzpatrick                 out_string(c, "CLIENT_ERROR bad command line format");
3160ecd931f2SSteven Grimm                 while (i-- > 0) {
3161ecd931f2SSteven Grimm                     item_remove(*(c->ilist + i));
3162ecd931f2SSteven Grimm                 }
316332f382b6SBrad Fitzpatrick                 return;
316432f382b6SBrad Fitzpatrick             }
316586969ea4SBrad Fitzpatrick 
31666895d23eSsergiocarlos             it = item_get(key, nkey, c);
316756b8339eSSteven Grimm             if (settings.detail_enabled) {
316846654a83STrond Norbye                 stats_prefix_record_get(key, nkey, NULL != it);
316956b8339eSSteven Grimm             }
317032f382b6SBrad Fitzpatrick             if (it) {
31710f2c1359SAndrei Nigmatulin                 if (i >= c->isize) {
31720f2c1359SAndrei Nigmatulin                     item **new_list = realloc(c->ilist, sizeof(item *) * c->isize * 2);
31730f2c1359SAndrei Nigmatulin                     if (new_list) {
31740f2c1359SAndrei Nigmatulin                         c->isize *= 2;
31750f2c1359SAndrei Nigmatulin                         c->ilist = new_list;
3176133d7f72Sdormando                     } else {
3177de021a9cSTrond Norbye                         STATS_LOCK();
3178de021a9cSTrond Norbye                         stats.malloc_fails++;
3179de021a9cSTrond Norbye                         STATS_UNLOCK();
3180133d7f72Sdormando                         item_remove(it);
3181133d7f72Sdormando                         break;
3182133d7f72Sdormando                     }
31830f2c1359SAndrei Nigmatulin                 }
318486969ea4SBrad Fitzpatrick 
3185c9607c6dSBrad Fitzpatrick                 /*
3186c9607c6dSBrad Fitzpatrick                  * Construct the response. Each hit adds three elements to the
3187c9607c6dSBrad Fitzpatrick                  * outgoing data list:
3188c9607c6dSBrad Fitzpatrick                  *   "VALUE "
3189c9607c6dSBrad Fitzpatrick                  *   key
3190c9607c6dSBrad Fitzpatrick                  *   " " + flags + " " + data length + "\r\n" + data (with \r\n)
3191c9607c6dSBrad Fitzpatrick                  */
3192fc2e8effSDustin Sallings 
3193f1351f9bSTrond Norbye                 if (return_cas)
3194fc2e8effSDustin Sallings                 {
319580ec0955STrond Norbye                   MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
3196eda68b70STrond Norbye                                         it->nbytes, ITEM_get_cas(it));
3197e61c0a86Sdormando                   /* Goofy mid-flight realloc. */
3198e61c0a86Sdormando                   if (i >= c->suffixsize) {
3199e61c0a86Sdormando                     char **new_suffix_list = realloc(c->suffixlist,
3200e61c0a86Sdormando                                            sizeof(char *) * c->suffixsize * 2);
3201e61c0a86Sdormando                     if (new_suffix_list) {
3202e61c0a86Sdormando                         c->suffixsize *= 2;
3203e61c0a86Sdormando                         c->suffixlist  = new_suffix_list;
3204133d7f72Sdormando                     } else {
3205de021a9cSTrond Norbye                         STATS_LOCK();
3206de021a9cSTrond Norbye                         stats.malloc_fails++;
3207de021a9cSTrond Norbye                         STATS_UNLOCK();
3208133d7f72Sdormando                         item_remove(it);
3209133d7f72Sdormando                         break;
3210133d7f72Sdormando                     }
3211e61c0a86Sdormando                   }
3212e61c0a86Sdormando 
32134c86fa59STrond Norbye                   suffix = cache_alloc(c->thread->suffix_cache);
3214e61c0a86Sdormando                   if (suffix == NULL) {
3215de021a9cSTrond Norbye                       STATS_LOCK();
3216de021a9cSTrond Norbye                       stats.malloc_fails++;
3217de021a9cSTrond Norbye                       STATS_UNLOCK();
3218d7757c3eSSteven Grimm                       out_of_memory(c, "SERVER_ERROR out of memory making CAS suffix");
3219133d7f72Sdormando                       item_remove(it);
3220ecd931f2SSteven Grimm                       while (i-- > 0) {
3221ecd931f2SSteven Grimm                           item_remove(*(c->ilist + i));
3222ecd931f2SSteven Grimm                       }
3223e61c0a86Sdormando                       return;
3224e61c0a86Sdormando                   }
3225e61c0a86Sdormando                   *(c->suffixlist + i) = suffix;
32260fd0bc18STrond Norbye                   int suffix_len = snprintf(suffix, SUFFIX_SIZE,
32270fd0bc18STrond Norbye                                             " %llu\r\n",
32280fd0bc18STrond Norbye                                             (unsigned long long)ITEM_get_cas(it));
3229fc2e8effSDustin Sallings                   if (add_iov(c, "VALUE ", 6) != 0 ||
3230fc2e8effSDustin Sallings                       add_iov(c, ITEM_key(it), it->nkey) != 0 ||
3231e61c0a86Sdormando                       add_iov(c, ITEM_suffix(it), it->nsuffix - 2) != 0 ||
3232b05653f9Sdormando                       add_iov(c, suffix, suffix_len) != 0)
3233fc2e8effSDustin Sallings                       {
3234133d7f72Sdormando                           item_remove(it);
3235fc2e8effSDustin Sallings                           break;
3236fc2e8effSDustin Sallings                       }
3237b05653f9Sdormando                   if ((it->it_flags & ITEM_CHUNKED) == 0) {
3238b05653f9Sdormando                       add_iov(c, ITEM_data(it), it->nbytes);
3239b05653f9Sdormando                   } else if (add_chunked_item_iovs(c, it, it->nbytes) != 0) {
3240b05653f9Sdormando                       item_remove(it);
3241b05653f9Sdormando                       break;
3242b05653f9Sdormando                   }
3243fc2e8effSDustin Sallings                 }
3244fc2e8effSDustin Sallings                 else
3245fc2e8effSDustin Sallings                 {
324680ec0955STrond Norbye                   MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
3247eda68b70STrond Norbye                                         it->nbytes, ITEM_get_cas(it));
324877dde9f9SPaul Lindner                   if (add_iov(c, "VALUE ", 6) != 0 ||
3249b05653f9Sdormando                       add_iov(c, ITEM_key(it), it->nkey) != 0)
3250c9607c6dSBrad Fitzpatrick                       {
3251133d7f72Sdormando                           item_remove(it);
3252c9607c6dSBrad Fitzpatrick                           break;
3253c9607c6dSBrad Fitzpatrick                       }
3254b05653f9Sdormando                   if ((it->it_flags & ITEM_CHUNKED) == 0)
3255b05653f9Sdormando                       {
3256b05653f9Sdormando                           if (add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0)
3257b05653f9Sdormando                           {
3258b05653f9Sdormando                               item_remove(it);
3259b05653f9Sdormando                               break;
3260b05653f9Sdormando                           }
3261b05653f9Sdormando                       } else if (add_iov(c, ITEM_suffix(it), it->nsuffix) != 0 ||
3262b05653f9Sdormando                                  add_chunked_item_iovs(c, it, it->nbytes) != 0) {
3263b05653f9Sdormando                           item_remove(it);
3264b05653f9Sdormando                           break;
3265b05653f9Sdormando                       }
3266fc2e8effSDustin Sallings                 }
3267fc2e8effSDustin Sallings 
3268fc2e8effSDustin Sallings 
3269fbe823d9Sdormando                 if (settings.verbose > 1) {
3270fbe823d9Sdormando                     int ii;
3271fbe823d9Sdormando                     fprintf(stderr, ">%d sending key ", c->sfd);
3272fbe823d9Sdormando                     for (ii = 0; ii < it->nkey; ++ii) {
3273fbe823d9Sdormando                         fprintf(stderr, "%c", key[ii]);
3274fbe823d9Sdormando                     }
3275fbe823d9Sdormando                     fprintf(stderr, "\n");
3276fbe823d9Sdormando                 }
327786969ea4SBrad Fitzpatrick 
327856b8339eSSteven Grimm                 /* item_get() has incremented it->refcount for us */
3279943f2e2bSTrond Norbye                 pthread_mutex_lock(&c->thread->stats.mutex);
32809bce42f2Sdormando                 c->thread->stats.slab_stats[ITEM_clsid(it)].get_hits++;
3281943f2e2bSTrond Norbye                 c->thread->stats.get_cmds++;
3282943f2e2bSTrond Norbye                 pthread_mutex_unlock(&c->thread->stats.mutex);
328332f382b6SBrad Fitzpatrick                 item_update(it);
328432f382b6SBrad Fitzpatrick                 *(c->ilist + i) = it;
328532f382b6SBrad Fitzpatrick                 i++;
3286217dcce0SSteven Grimm 
328756b8339eSSteven Grimm             } else {
3288943f2e2bSTrond Norbye                 pthread_mutex_lock(&c->thread->stats.mutex);
3289943f2e2bSTrond Norbye                 c->thread->stats.get_misses++;
3290b1001be0STrond Norbye                 c->thread->stats.get_cmds++;
3291943f2e2bSTrond Norbye                 pthread_mutex_unlock(&c->thread->stats.mutex);
329280ec0955STrond Norbye                 MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
329356b8339eSSteven Grimm             }
3294217dcce0SSteven Grimm 
3295217dcce0SSteven Grimm             key_token++;
329632f382b6SBrad Fitzpatrick         }
329786969ea4SBrad Fitzpatrick 
3298217dcce0SSteven Grimm         /*
3299217dcce0SSteven Grimm          * If the command string hasn't been fully processed, get the next set
3300217dcce0SSteven Grimm          * of tokens.
3301217dcce0SSteven Grimm          */
3302217dcce0SSteven Grimm         if(key_token->value != NULL) {
3303217dcce0SSteven Grimm             ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS);
3304217dcce0SSteven Grimm             key_token = tokens;
3305217dcce0SSteven Grimm         }
3306217dcce0SSteven Grimm 
3307217dcce0SSteven Grimm     } while(key_token->value != NULL);
3308217dcce0SSteven Grimm 
330932f382b6SBrad Fitzpatrick     c->icurr = c->ilist;
331032f382b6SBrad Fitzpatrick     c->ileft = i;
3311e61c0a86Sdormando     if (return_cas) {
3312e61c0a86Sdormando         c->suffixcurr = c->suffixlist;
3313e61c0a86Sdormando         c->suffixleft = i;
3314e61c0a86Sdormando     }
331586969ea4SBrad Fitzpatrick 
3316c9607c6dSBrad Fitzpatrick     if (settings.verbose > 1)
3317c9607c6dSBrad Fitzpatrick         fprintf(stderr, ">%d END\n", c->sfd);
331886969ea4SBrad Fitzpatrick 
331984dfb21fSdormando     /*
332084dfb21fSdormando         If the loop was terminated because of out-of-memory, it is not
332184dfb21fSdormando         reliable to add END\r\n to the buffer, because it might not end
332284dfb21fSdormando         in \r\n. So we send SERVER_ERROR instead.
332384dfb21fSdormando     */
332484dfb21fSdormando     if (key_token->value != NULL || add_iov(c, "END\r\n", 5) != 0
332515ace4b5SEric Lambert         || (IS_UDP(c->transport) && build_udp_headers(c) != 0)) {
3326d7757c3eSSteven Grimm         out_of_memory(c, "SERVER_ERROR out of memory writing get response");
332732f382b6SBrad Fitzpatrick     }
3328c9607c6dSBrad Fitzpatrick     else {
3329c9607c6dSBrad Fitzpatrick         conn_set_state(c, conn_mwrite);
3330c9607c6dSBrad Fitzpatrick         c->msgcurr = 0;
3331c9607c6dSBrad Fitzpatrick     }
333232f382b6SBrad Fitzpatrick }
333386969ea4SBrad Fitzpatrick 
process_update_command(conn * c,token_t * tokens,const size_t ntokens,int comm,bool handle_cas)3334fc2e8effSDustin Sallings static void process_update_command(conn *c, token_t *tokens, const size_t ntokens, int comm, bool handle_cas) {
3335217dcce0SSteven Grimm     char *key;
3336217dcce0SSteven Grimm     size_t nkey;
33370fe0928bSDustin Sallings     unsigned int flags;
33380fe0928bSDustin Sallings     int32_t exptime_int = 0;
3339217dcce0SSteven Grimm     time_t exptime;
3340df1b7e42STrond Norbye     int vlen;
3341de982a70SDustin Sallings     uint64_t req_cas_id=0;
3342df1b7e42STrond Norbye     item *it;
3343217dcce0SSteven Grimm 
334478955139STim Yardley     assert(c != NULL);
334578955139STim Yardley 
3346d9ece780STomash Brechko     set_noreply_maybe(c, tokens, ntokens);
3347d9ece780STomash Brechko 
3348217dcce0SSteven Grimm     if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
3349217dcce0SSteven Grimm         out_string(c, "CLIENT_ERROR bad command line format");
3350217dcce0SSteven Grimm         return;
3351217dcce0SSteven Grimm     }
3352217dcce0SSteven Grimm 
3353217dcce0SSteven Grimm     key = tokens[KEY_TOKEN].value;
3354217dcce0SSteven Grimm     nkey = tokens[KEY_TOKEN].length;
3355217dcce0SSteven Grimm 
33560fe0928bSDustin Sallings     if (! (safe_strtoul(tokens[2].value, (uint32_t *)&flags)
33570fe0928bSDustin Sallings            && safe_strtol(tokens[3].value, &exptime_int)
33580fe0928bSDustin Sallings            && safe_strtol(tokens[4].value, (int32_t *)&vlen))) {
33590fe0928bSDustin Sallings         out_string(c, "CLIENT_ERROR bad command line format");
33600fe0928bSDustin Sallings         return;
33610fe0928bSDustin Sallings     }
33620fe0928bSDustin Sallings 
33630fe0928bSDustin Sallings     /* Ubuntu 8.04 breaks when I pass exptime to safe_strtol */
33640fe0928bSDustin Sallings     exptime = exptime_int;
3365217dcce0SSteven Grimm 
336636b83f87Sdormando     /* Negative exptimes can underflow and end up immortal. realtime() will
336736b83f87Sdormando        immediately expire values that are greater than REALTIME_MAXDELTA, but less
336836b83f87Sdormando        than process_started, so lets aim for that. */
336936b83f87Sdormando     if (exptime < 0)
337036b83f87Sdormando         exptime = REALTIME_MAXDELTA + 1;
337136b83f87Sdormando 
3372fc2e8effSDustin Sallings     // does cas value exist?
3373de982a70SDustin Sallings     if (handle_cas) {
3374483e82adSdormando         if (!safe_strtoull(tokens[5].value, &req_cas_id)) {
3375217dcce0SSteven Grimm             out_string(c, "CLIENT_ERROR bad command line format");
3376217dcce0SSteven Grimm             return;
3377217dcce0SSteven Grimm         }
33780fe0928bSDustin Sallings     }
3379217dcce0SSteven Grimm 
338009d5afadSJ. Grizzard     if (vlen < 0 || vlen > (INT_MAX - 2)) {
3381483e82adSdormando         out_string(c, "CLIENT_ERROR bad command line format");
3382483e82adSdormando         return;
3383483e82adSdormando     }
338409d5afadSJ. Grizzard     vlen += 2;
3385483e82adSdormando 
338656b8339eSSteven Grimm     if (settings.detail_enabled) {
338746654a83STrond Norbye         stats_prefix_record_set(key, nkey);
338856b8339eSSteven Grimm     }
338956b8339eSSteven Grimm 
3390483e82adSdormando     it = item_alloc(key, nkey, flags, realtime(exptime), vlen);
3391217dcce0SSteven Grimm 
3392217dcce0SSteven Grimm     if (it == 0) {
3393c7fbccebSdormando         enum store_item_type status;
3394c7fbccebSdormando         if (! item_size_ok(nkey, flags, vlen)) {
3395217dcce0SSteven Grimm             out_string(c, "SERVER_ERROR object too large for cache");
3396c7fbccebSdormando             status = TOO_LARGE;
3397c7fbccebSdormando         } else {
3398d7757c3eSSteven Grimm             out_of_memory(c, "SERVER_ERROR out of memory storing object");
3399c7fbccebSdormando             status = NO_MEMORY;
3400c7fbccebSdormando         }
3401c7fbccebSdormando         LOGGER_LOG(c->thread->l, LOG_MUTATIONS, LOGGER_ITEM_STORE,
3402c7fbccebSdormando                 NULL, status, comm, key, nkey);
3403217dcce0SSteven Grimm         /* swallow the data line */
3404217dcce0SSteven Grimm         c->write_and_go = conn_swallow;
3405483e82adSdormando         c->sbytes = vlen;
34065f54ede8Sdormando 
34075f54ede8Sdormando         /* Avoid stale data persisting in cache because we failed alloc.
34085f54ede8Sdormando          * Unacceptable for SET. Anywhere else too? */
34095f54ede8Sdormando         if (comm == NREAD_SET) {
34106895d23eSsergiocarlos             it = item_get(key, nkey, c);
34115f54ede8Sdormando             if (it) {
34125f54ede8Sdormando                 item_unlink(it);
34135f54ede8Sdormando                 item_remove(it);
34145f54ede8Sdormando             }
34155f54ede8Sdormando         }
34165f54ede8Sdormando 
3417217dcce0SSteven Grimm         return;
3418217dcce0SSteven Grimm     }
3419eda68b70STrond Norbye     ITEM_set_cas(it, req_cas_id);
3420217dcce0SSteven Grimm 
3421217dcce0SSteven Grimm     c->item = it;
3422217dcce0SSteven Grimm     c->ritem = ITEM_data(it);
3423217dcce0SSteven Grimm     c->rlbytes = it->nbytes;
34240a77fdfaSDustin Sallings     c->cmd = comm;
3425217dcce0SSteven Grimm     conn_set_state(c, conn_nread);
3426217dcce0SSteven Grimm }
3427217dcce0SSteven Grimm 
process_touch_command(conn * c,token_t * tokens,const size_t ntokens)3428648a07f9Sdormando static void process_touch_command(conn *c, token_t *tokens, const size_t ntokens) {
3429648a07f9Sdormando     char *key;
3430648a07f9Sdormando     size_t nkey;
3431648a07f9Sdormando     int32_t exptime_int = 0;
3432648a07f9Sdormando     item *it;
3433648a07f9Sdormando 
3434648a07f9Sdormando     assert(c != NULL);
3435648a07f9Sdormando 
3436648a07f9Sdormando     set_noreply_maybe(c, tokens, ntokens);
3437648a07f9Sdormando 
3438648a07f9Sdormando     if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
3439648a07f9Sdormando         out_string(c, "CLIENT_ERROR bad command line format");
3440648a07f9Sdormando         return;
3441648a07f9Sdormando     }
3442648a07f9Sdormando 
3443648a07f9Sdormando     key = tokens[KEY_TOKEN].value;
3444648a07f9Sdormando     nkey = tokens[KEY_TOKEN].length;
3445648a07f9Sdormando 
3446648a07f9Sdormando     if (!safe_strtol(tokens[2].value, &exptime_int)) {
3447648a07f9Sdormando         out_string(c, "CLIENT_ERROR invalid exptime argument");
3448648a07f9Sdormando         return;
3449648a07f9Sdormando     }
3450648a07f9Sdormando 
34516895d23eSsergiocarlos     it = item_touch(key, nkey, realtime(exptime_int), c);
3452648a07f9Sdormando     if (it) {
3453c9055e84Sdormando         item_update(it);
3454648a07f9Sdormando         pthread_mutex_lock(&c->thread->stats.mutex);
3455648a07f9Sdormando         c->thread->stats.touch_cmds++;
34569bce42f2Sdormando         c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
3457648a07f9Sdormando         pthread_mutex_unlock(&c->thread->stats.mutex);
3458648a07f9Sdormando 
3459648a07f9Sdormando         out_string(c, "TOUCHED");
3460648a07f9Sdormando         item_remove(it);
3461648a07f9Sdormando     } else {
3462648a07f9Sdormando         pthread_mutex_lock(&c->thread->stats.mutex);
3463648a07f9Sdormando         c->thread->stats.touch_cmds++;
3464648a07f9Sdormando         c->thread->stats.touch_misses++;
3465648a07f9Sdormando         pthread_mutex_unlock(&c->thread->stats.mutex);
3466648a07f9Sdormando 
3467648a07f9Sdormando         out_string(c, "NOT_FOUND");
3468648a07f9Sdormando     }
3469648a07f9Sdormando }
3470648a07f9Sdormando 
process_arithmetic_command(conn * c,token_t * tokens,const size_t ntokens,const bool incr)347114280226SEvan Miller static void process_arithmetic_command(conn *c, token_t *tokens, const size_t ntokens, const bool incr) {
34727b7bc241SDustin Sallings     char temp[INCR_MAX_STORAGE_LEN];
347324fda0fcSDustin Sallings     uint64_t delta;
3474217dcce0SSteven Grimm     char *key;
3475217dcce0SSteven Grimm     size_t nkey;
3476217dcce0SSteven Grimm 
347778955139STim Yardley     assert(c != NULL);
347878955139STim Yardley 
3479d9ece780STomash Brechko     set_noreply_maybe(c, tokens, ntokens);
3480d9ece780STomash Brechko 
3481217dcce0SSteven Grimm     if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
3482217dcce0SSteven Grimm         out_string(c, "CLIENT_ERROR bad command line format");
3483217dcce0SSteven Grimm         return;
3484217dcce0SSteven Grimm     }
3485217dcce0SSteven Grimm 
3486217dcce0SSteven Grimm     key = tokens[KEY_TOKEN].value;
3487217dcce0SSteven Grimm     nkey = tokens[KEY_TOKEN].length;
3488217dcce0SSteven Grimm 
348924fda0fcSDustin Sallings     if (!safe_strtoull(tokens[2].value, &delta)) {
3490674166c0SBrad Fitzpatrick         out_string(c, "CLIENT_ERROR invalid numeric delta argument");
3491217dcce0SSteven Grimm         return;
3492217dcce0SSteven Grimm     }
3493217dcce0SSteven Grimm 
3494ea2d42a5Sdormando     switch(add_delta(c, key, nkey, incr, delta, temp, NULL)) {
3495d044acb2SDustin Sallings     case OK:
3496d044acb2SDustin Sallings         out_string(c, temp);
3497d044acb2SDustin Sallings         break;
3498d044acb2SDustin Sallings     case NON_NUMERIC:
3499d044acb2SDustin Sallings         out_string(c, "CLIENT_ERROR cannot increment or decrement non-numeric value");
3500d044acb2SDustin Sallings         break;
3501d044acb2SDustin Sallings     case EOM:
3502d7757c3eSSteven Grimm         out_of_memory(c, "SERVER_ERROR out of memory");
3503d044acb2SDustin Sallings         break;
3504cbcd3872Sdormando     case DELTA_ITEM_NOT_FOUND:
3505cbcd3872Sdormando         pthread_mutex_lock(&c->thread->stats.mutex);
3506cbcd3872Sdormando         if (incr) {
3507cbcd3872Sdormando             c->thread->stats.incr_misses++;
3508cbcd3872Sdormando         } else {
3509cbcd3872Sdormando             c->thread->stats.decr_misses++;
3510d044acb2SDustin Sallings         }
3511cbcd3872Sdormando         pthread_mutex_unlock(&c->thread->stats.mutex);
3512cbcd3872Sdormando 
3513cbcd3872Sdormando         out_string(c, "NOT_FOUND");
3514cbcd3872Sdormando         break;
3515ea2d42a5Sdormando     case DELTA_ITEM_CAS_MISMATCH:
3516ea2d42a5Sdormando         break; /* Should never get here */
3517cbcd3872Sdormando     }
351856b8339eSSteven Grimm }
351956b8339eSSteven Grimm 
352056b8339eSSteven Grimm /*
352156b8339eSSteven Grimm  * adds a delta value to a numeric item.
352256b8339eSSteven Grimm  *
352368957214STrond Norbye  * c     connection requesting the operation
352456b8339eSSteven Grimm  * it    item to adjust
352556b8339eSSteven Grimm  * incr  true to increment value, false to decrement
352656b8339eSSteven Grimm  * delta amount to adjust value by
352756b8339eSSteven Grimm  * buf   buffer for response string
352856b8339eSSteven Grimm  *
352956b8339eSSteven Grimm  * returns a response string to send back to the client.
353056b8339eSSteven Grimm  */
do_add_delta(conn * c,const char * key,const size_t nkey,const bool incr,const int64_t delta,char * buf,uint64_t * cas,const uint32_t hv)3531cbcd3872Sdormando enum delta_result_type do_add_delta(conn *c, const char *key, const size_t nkey,
3532cbcd3872Sdormando                                     const bool incr, const int64_t delta,
3533bab9acd1Sdormando                                     char *buf, uint64_t *cas,
3534bab9acd1Sdormando                                     const uint32_t hv) {
353556b8339eSSteven Grimm     char *ptr;
3536013e17ccSDustin Sallings     uint64_t value;
353756b8339eSSteven Grimm     int res;
3538cbcd3872Sdormando     item *it;
3539cbcd3872Sdormando 
35406895d23eSsergiocarlos     it = do_item_get(key, nkey, hv, c);
3541cbcd3872Sdormando     if (!it) {
3542cbcd3872Sdormando         return DELTA_ITEM_NOT_FOUND;
3543cbcd3872Sdormando     }
354456b8339eSSteven Grimm 
354558d05f1dSdormando     /* Can't delta zero byte values. 2-byte are the "\r\n" */
35460567967aSdormando     /* Also can't delta for chunked items. Too large to be a number */
35470567967aSdormando     if (it->nbytes <= 2 || (it->it_flags & ITEM_CHUNKED) != 0) {
354858d05f1dSdormando         return NON_NUMERIC;
354958d05f1dSdormando     }
355058d05f1dSdormando 
3551ea2d42a5Sdormando     if (cas != NULL && *cas != 0 && ITEM_get_cas(it) != *cas) {
3552ea2d42a5Sdormando         do_item_remove(it);
3553ea2d42a5Sdormando         return DELTA_ITEM_CAS_MISMATCH;
3554ea2d42a5Sdormando     }
3555ea2d42a5Sdormando 
3556217dcce0SSteven Grimm     ptr = ITEM_data(it);
3557217dcce0SSteven Grimm 
3558674166c0SBrad Fitzpatrick     if (!safe_strtoull(ptr, &value)) {
3559cbcd3872Sdormando         do_item_remove(it);
3560d044acb2SDustin Sallings         return NON_NUMERIC;
3561217dcce0SSteven Grimm     }
3562217dcce0SSteven Grimm 
356368957214STrond Norbye     if (incr) {
3564217dcce0SSteven Grimm         value += delta;
356580ec0955STrond Norbye         MEMCACHED_COMMAND_INCR(c->sfd, ITEM_key(it), it->nkey, value);
356668957214STrond Norbye     } else {
3567013e17ccSDustin Sallings         if(delta > value) {
35686aafe58eSDustin Sallings             value = 0;
3569013e17ccSDustin Sallings         } else {
3570013e17ccSDustin Sallings             value -= delta;
3571217dcce0SSteven Grimm         }
357280ec0955STrond Norbye         MEMCACHED_COMMAND_DECR(c->sfd, ITEM_key(it), it->nkey, value);
357368957214STrond Norbye     }
35743e030782SDustin Sallings 
35753e030782SDustin Sallings     pthread_mutex_lock(&c->thread->stats.mutex);
35763e030782SDustin Sallings     if (incr) {
35779bce42f2Sdormando         c->thread->stats.slab_stats[ITEM_clsid(it)].incr_hits++;
35783e030782SDustin Sallings     } else {
35799bce42f2Sdormando         c->thread->stats.slab_stats[ITEM_clsid(it)].decr_hits++;
35803e030782SDustin Sallings     }
35813e030782SDustin Sallings     pthread_mutex_unlock(&c->thread->stats.mutex);
35823e030782SDustin Sallings 
35837b7bc241SDustin Sallings     snprintf(buf, INCR_MAX_STORAGE_LEN, "%llu", (unsigned long long)value);
358456b8339eSSteven Grimm     res = strlen(buf);
3585cb9c269bSdormando     /* refcount == 2 means we are the only ones holding the item, and it is
3586facb719fSdormando      * linked. We hold the item's lock in this function, so refcount cannot
3587facb719fSdormando      * increase. */
3588facb719fSdormando     if (res + 2 <= it->nbytes && it->refcount == 2) { /* replace in-place */
3589facb719fSdormando         /* When changing the value without replacing the item, we
3590facb719fSdormando            need to update the CAS on the existing item. */
35918d82383fSdormando         /* We also need to fiddle it in the sizes tracker in case the tracking
35928d82383fSdormando          * was enabled at runtime, since it relies on the CAS value to know
35938d82383fSdormando          * whether to remove an item or not. */
35948d82383fSdormando         item_stats_sizes_remove(it);
3595facb719fSdormando         ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
35968d82383fSdormando         item_stats_sizes_add(it);
3597facb719fSdormando         memcpy(ITEM_data(it), buf, res);
3598facb719fSdormando         memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2);
3599facb719fSdormando         do_item_update(it);
3600facb719fSdormando     } else if (it->refcount > 1) {
3601217dcce0SSteven Grimm         item *new_it;
3602181ef834Sdormando         uint32_t flags = (uint32_t) strtoul(ITEM_suffix(it)+1, (char **) NULL, 10);
3603*690a9a9dSEiichi Tsukata         new_it = do_item_alloc(ITEM_key(it), it->nkey, flags, it->exptime, res + 2);
3604217dcce0SSteven Grimm         if (new_it == 0) {
3605cbcd3872Sdormando             do_item_remove(it);
3606d044acb2SDustin Sallings             return EOM;
3607217dcce0SSteven Grimm         }
360856b8339eSSteven Grimm         memcpy(ITEM_data(new_it), buf, res);
36092d61f185STomash Brechko         memcpy(ITEM_data(new_it) + res, "\r\n", 2);
3610bab9acd1Sdormando         item_replace(it, new_it, hv);
36112096c491SDustin Sallings         // Overwrite the older item's CAS with our new CAS since we're
36122096c491SDustin Sallings         // returning the CAS of the old item below.
36132096c491SDustin Sallings         ITEM_set_cas(it, (settings.use_cas) ? ITEM_get_cas(new_it) : 0);
361456b8339eSSteven Grimm         do_item_remove(new_it);       /* release our reference */
3615facb719fSdormando     } else {
3616facb719fSdormando         /* Should never get here. This means we somehow fetched an unlinked
3617facb719fSdormando          * item. TODO: Add a counter? */
3618facb719fSdormando         if (settings.verbose) {
3619facb719fSdormando             fprintf(stderr, "Tried to do incr/decr on invalid item\n");
3620facb719fSdormando         }
3621facb719fSdormando         if (it->refcount == 1)
3622facb719fSdormando             do_item_remove(it);
3623facb719fSdormando         return DELTA_ITEM_NOT_FOUND;
3624217dcce0SSteven Grimm     }
362556b8339eSSteven Grimm 
3626ea2d42a5Sdormando     if (cas) {
3627ea2d42a5Sdormando         *cas = ITEM_get_cas(it);    /* swap the incoming CAS value */
3628ea2d42a5Sdormando     }
3629cbcd3872Sdormando     do_item_remove(it);         /* release our reference */
3630d044acb2SDustin Sallings     return OK;
3631217dcce0SSteven Grimm }
3632217dcce0SSteven Grimm 
process_delete_command(conn * c,token_t * tokens,const size_t ntokens)363377dde9f9SPaul Lindner static void process_delete_command(conn *c, token_t *tokens, const size_t ntokens) {
3634217dcce0SSteven Grimm     char *key;
3635217dcce0SSteven Grimm     size_t nkey;
3636217dcce0SSteven Grimm     item *it;
363786969ea4SBrad Fitzpatrick 
363878955139STim Yardley     assert(c != NULL);
363978955139STim Yardley 
364062a0cf9eSDustin Sallings     if (ntokens > 3) {
364162a0cf9eSDustin Sallings         bool hold_is_zero = strcmp(tokens[KEY_TOKEN+1].value, "0") == 0;
364262a0cf9eSDustin Sallings         bool sets_noreply = set_noreply_maybe(c, tokens, ntokens);
364362a0cf9eSDustin Sallings         bool valid = (ntokens == 4 && (hold_is_zero || sets_noreply))
364462a0cf9eSDustin Sallings             || (ntokens == 5 && hold_is_zero && sets_noreply);
364562a0cf9eSDustin Sallings         if (!valid) {
3646ac37e81cSDustin Sallings             out_string(c, "CLIENT_ERROR bad command line format.  "
3647ac37e81cSDustin Sallings                        "Usage: delete <key> [noreply]");
3648dd599c0eSDustin Sallings             return;
3649dd599c0eSDustin Sallings         }
3650dd599c0eSDustin Sallings     }
3651d9ece780STomash Brechko 
365262a0cf9eSDustin Sallings 
3653217dcce0SSteven Grimm     key = tokens[KEY_TOKEN].value;
3654217dcce0SSteven Grimm     nkey = tokens[KEY_TOKEN].length;
3655217dcce0SSteven Grimm 
3656217dcce0SSteven Grimm     if(nkey > KEY_MAX_LENGTH) {
3657217dcce0SSteven Grimm         out_string(c, "CLIENT_ERROR bad command line format");
3658217dcce0SSteven Grimm         return;
3659217dcce0SSteven Grimm     }
3660217dcce0SSteven Grimm 
366156b8339eSSteven Grimm     if (settings.detail_enabled) {
366246654a83STrond Norbye         stats_prefix_record_delete(key, nkey);
3663b5e37760SBrad Fitzpatrick     }
3664217dcce0SSteven Grimm 
36656895d23eSsergiocarlos     it = item_get(key, nkey, c);
366656b8339eSSteven Grimm     if (it) {
366780ec0955STrond Norbye         MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
3668a77d12b0SDustin Sallings 
3669a77d12b0SDustin Sallings         pthread_mutex_lock(&c->thread->stats.mutex);
36709bce42f2Sdormando         c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
3671a77d12b0SDustin Sallings         pthread_mutex_unlock(&c->thread->stats.mutex);
3672a77d12b0SDustin Sallings 
3673585d68caSMichael Alan Dorman         item_unlink(it);
367456b8339eSSteven Grimm         item_remove(it);      /* release our reference */
3675585d68caSMichael Alan Dorman         out_string(c, "DELETED");
367656b8339eSSteven Grimm     } else {
3677a77d12b0SDustin Sallings         pthread_mutex_lock(&c->thread->stats.mutex);
3678a77d12b0SDustin Sallings         c->thread->stats.delete_misses++;
3679a77d12b0SDustin Sallings         pthread_mutex_unlock(&c->thread->stats.mutex);
3680a77d12b0SDustin Sallings 
368156b8339eSSteven Grimm         out_string(c, "NOT_FOUND");
368256b8339eSSteven Grimm     }
368356b8339eSSteven Grimm }
368456b8339eSSteven Grimm 
process_verbosity_command(conn * c,token_t * tokens,const size_t ntokens)3685eca55c9aSPaul Lindner static void process_verbosity_command(conn *c, token_t *tokens, const size_t ntokens) {
3686eca55c9aSPaul Lindner     unsigned int level;
3687eca55c9aSPaul Lindner 
3688eca55c9aSPaul Lindner     assert(c != NULL);
3689eca55c9aSPaul Lindner 
3690d9ece780STomash Brechko     set_noreply_maybe(c, tokens, ntokens);
3691d9ece780STomash Brechko 
3692eca55c9aSPaul Lindner     level = strtoul(tokens[1].value, NULL, 10);
3693eca55c9aSPaul Lindner     settings.verbose = level > MAX_VERBOSITY_LEVEL ? MAX_VERBOSITY_LEVEL : level;
3694c0ec7b09SPaolo Borelli     out_string(c, "OK");
3695eca55c9aSPaul Lindner     return;
3696eca55c9aSPaul Lindner }
3697eca55c9aSPaul Lindner 
process_slabs_automove_command(conn * c,token_t * tokens,const size_t ntokens)369899fc043aSdormando static void process_slabs_automove_command(conn *c, token_t *tokens, const size_t ntokens) {
369999fc043aSdormando     unsigned int level;
370099fc043aSdormando 
370199fc043aSdormando     assert(c != NULL);
370299fc043aSdormando 
370399fc043aSdormando     set_noreply_maybe(c, tokens, ntokens);
370499fc043aSdormando 
370599fc043aSdormando     level = strtoul(tokens[2].value, NULL, 10);
370699fc043aSdormando     if (level == 0) {
370763bf748aSdormando         settings.slab_automove = 0;
370863bf748aSdormando     } else if (level == 1 || level == 2) {
370963bf748aSdormando         settings.slab_automove = level;
371099fc043aSdormando     } else {
371199fc043aSdormando         out_string(c, "ERROR");
371299fc043aSdormando         return;
371399fc043aSdormando     }
371499fc043aSdormando     out_string(c, "OK");
371599fc043aSdormando     return;
371699fc043aSdormando }
371799fc043aSdormando 
3718efa436feSdormando /* TODO: decide on syntax for sampling? */
process_watch_command(conn * c,token_t * tokens,const size_t ntokens)3719efa436feSdormando static void process_watch_command(conn *c, token_t *tokens, const size_t ntokens) {
3720efa436feSdormando     uint16_t f = 0;
3721efa436feSdormando     int x;
3722efa436feSdormando     assert(c != NULL);
3723efa436feSdormando 
3724efa436feSdormando     set_noreply_maybe(c, tokens, ntokens);
3725efa436feSdormando     if (ntokens > 2) {
3726efa436feSdormando         for (x = COMMAND_TOKEN + 1; x < ntokens - 1; x++) {
3727efa436feSdormando             if ((strcmp(tokens[x].value, "rawcmds") == 0)) {
3728efa436feSdormando                 f |= LOG_RAWCMDS;
3729efa436feSdormando             } else if ((strcmp(tokens[x].value, "evictions") == 0)) {
3730efa436feSdormando                 f |= LOG_EVICTIONS;
3731c7fbccebSdormando             } else if ((strcmp(tokens[x].value, "fetchers") == 0)) {
3732c7fbccebSdormando                 f |= LOG_FETCHERS;
3733c7fbccebSdormando             } else if ((strcmp(tokens[x].value, "mutations") == 0)) {
3734c7fbccebSdormando                 f |= LOG_MUTATIONS;
3735efa436feSdormando             } else {
3736efa436feSdormando                 out_string(c, "ERROR");
3737efa436feSdormando                 return;
3738efa436feSdormando             }
3739efa436feSdormando         }
3740efa436feSdormando     } else {
3741c7fbccebSdormando         f |= LOG_FETCHERS;
3742efa436feSdormando     }
3743efa436feSdormando 
3744efa436feSdormando     switch(logger_add_watcher(c, c->sfd, f)) {
3745efa436feSdormando         case LOGGER_ADD_WATCHER_TOO_MANY:
3746efa436feSdormando             out_string(c, "WATCHER_TOO_MANY log watcher limit reached");
3747efa436feSdormando             break;
3748efa436feSdormando         case LOGGER_ADD_WATCHER_FAILED:
3749efa436feSdormando             out_string(c, "WATCHER_FAILED failed to add log watcher");
3750efa436feSdormando             break;
3751efa436feSdormando         case LOGGER_ADD_WATCHER_OK:
3752efa436feSdormando             conn_set_state(c, conn_watch);
3753efa436feSdormando             event_del(&c->event);
3754efa436feSdormando             break;
3755efa436feSdormando     }
3756efa436feSdormando }
3757efa436feSdormando 
process_memlimit_command(conn * c,token_t * tokens,const size_t ntokens)375831541b37Sdormando static void process_memlimit_command(conn *c, token_t *tokens, const size_t ntokens) {
375931541b37Sdormando     uint32_t memlimit;
376031541b37Sdormando     assert(c != NULL);
376131541b37Sdormando 
376231541b37Sdormando     set_noreply_maybe(c, tokens, ntokens);
376331541b37Sdormando 
376431541b37Sdormando     if (!safe_strtoul(tokens[1].value, &memlimit)) {
376531541b37Sdormando         out_string(c, "ERROR");
376631541b37Sdormando     } else {
376731541b37Sdormando         if (memlimit < 8) {
376831541b37Sdormando             out_string(c, "MEMLIMIT_TOO_SMALL cannot set maxbytes to less than 8m");
376931541b37Sdormando         } else {
377031541b37Sdormando             if (slabs_adjust_mem_limit(memlimit * 1024 * 1024)) {
377131541b37Sdormando                 if (settings.verbose > 0) {
377231541b37Sdormando                     fprintf(stderr, "maxbytes adjusted to %llum\n", (unsigned long long)memlimit);
377331541b37Sdormando                 }
377431541b37Sdormando 
377531541b37Sdormando                 out_string(c, "OK");
377631541b37Sdormando             } else {
377731541b37Sdormando                 out_string(c, "MEMLIMIT_ADJUST_FAILED out of bounds or unable to adjust");
377831541b37Sdormando             }
377931541b37Sdormando         }
378031541b37Sdormando     }
378131541b37Sdormando }
378231541b37Sdormando 
process_command(conn * c,char * command)378377dde9f9SPaul Lindner static void process_command(conn *c, char *command) {
3784217dcce0SSteven Grimm 
3785217dcce0SSteven Grimm     token_t tokens[MAX_TOKENS];
3786217dcce0SSteven Grimm     size_t ntokens;
3787217dcce0SSteven Grimm     int comm;
3788217dcce0SSteven Grimm 
378978955139STim Yardley     assert(c != NULL);
379078955139STim Yardley 
379168957214STrond Norbye     MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
379268957214STrond Norbye 
3793c7fbccebSdormando     if (settings.verbose > 1)
3794c7fbccebSdormando         fprintf(stderr, "<%d %s\n", c->sfd, command);
3795217dcce0SSteven Grimm 
3796217dcce0SSteven Grimm     /*
3797217dcce0SSteven Grimm      * for commands set/add/replace, we build an item and read the data
3798217dcce0SSteven Grimm      * directly into it, then continue in nread_complete().
3799217dcce0SSteven Grimm      */
3800217dcce0SSteven Grimm 
3801217dcce0SSteven Grimm     c->msgcurr = 0;
3802217dcce0SSteven Grimm     c->msgused = 0;
3803217dcce0SSteven Grimm     c->iovused = 0;
380477dde9f9SPaul Lindner     if (add_msghdr(c) != 0) {
3805d7757c3eSSteven Grimm         out_of_memory(c, "SERVER_ERROR out of memory preparing response");
3806217dcce0SSteven Grimm         return;
3807217dcce0SSteven Grimm     }
3808217dcce0SSteven Grimm 
3809217dcce0SSteven Grimm     ntokens = tokenize_command(command, tokens, MAX_TOKENS);
3810217dcce0SSteven Grimm     if (ntokens >= 3 &&
3811217dcce0SSteven Grimm         ((strcmp(tokens[COMMAND_TOKEN].value, "get") == 0) ||
3812217dcce0SSteven Grimm          (strcmp(tokens[COMMAND_TOKEN].value, "bget") == 0))) {
3813217dcce0SSteven Grimm 
3814fc2e8effSDustin Sallings         process_get_command(c, tokens, ntokens, false);
3815217dcce0SSteven Grimm 
3816d9ece780STomash Brechko     } else if ((ntokens == 6 || ntokens == 7) &&
3817217dcce0SSteven Grimm                ((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) ||
3818217dcce0SSteven Grimm                 (strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) ||
38197283d85bSFilipe Laborde                 (strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)) ||
38206091c6deSPaul Lindner                 (strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) ||
38217283d85bSFilipe Laborde                 (strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) )) {
3822217dcce0SSteven Grimm 
3823fc2e8effSDustin Sallings         process_update_command(c, tokens, ntokens, comm, false);
3824fc2e8effSDustin Sallings 
3825d9ece780STomash Brechko     } else if ((ntokens == 7 || ntokens == 8) && (strcmp(tokens[COMMAND_TOKEN].value, "cas") == 0 && (comm = NREAD_CAS))) {
3826fc2e8effSDustin Sallings 
3827fc2e8effSDustin Sallings         process_update_command(c, tokens, ntokens, comm, true);
3828217dcce0SSteven Grimm 
3829d9ece780STomash Brechko     } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0)) {
3830217dcce0SSteven Grimm 
3831217dcce0SSteven Grimm         process_arithmetic_command(c, tokens, ntokens, 1);
3832217dcce0SSteven Grimm 
3833fc2e8effSDustin Sallings     } else if (ntokens >= 3 && (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0)) {
3834fc2e8effSDustin Sallings 
3835fc2e8effSDustin Sallings         process_get_command(c, tokens, ntokens, true);
3836fc2e8effSDustin Sallings 
3837d9ece780STomash Brechko     } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "decr") == 0)) {
3838217dcce0SSteven Grimm 
3839217dcce0SSteven Grimm         process_arithmetic_command(c, tokens, ntokens, 0);
3840217dcce0SSteven Grimm 
384162a0cf9eSDustin Sallings     } else if (ntokens >= 3 && ntokens <= 5 && (strcmp(tokens[COMMAND_TOKEN].value, "delete") == 0)) {
3842217dcce0SSteven Grimm 
3843217dcce0SSteven Grimm         process_delete_command(c, tokens, ntokens);
3844217dcce0SSteven Grimm 
3845648a07f9Sdormando     } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "touch") == 0)) {
3846648a07f9Sdormando 
3847648a07f9Sdormando         process_touch_command(c, tokens, ntokens);
3848648a07f9Sdormando 
3849217dcce0SSteven Grimm     } else if (ntokens >= 2 && (strcmp(tokens[COMMAND_TOKEN].value, "stats") == 0)) {
385086969ea4SBrad Fitzpatrick 
3851217dcce0SSteven Grimm         process_stat(c, tokens, ntokens);
3852217dcce0SSteven Grimm 
3853d9ece780STomash Brechko     } else if (ntokens >= 2 && ntokens <= 4 && (strcmp(tokens[COMMAND_TOKEN].value, "flush_all") == 0)) {
385454c9113bSElizabeth Mattijsen         time_t exptime = 0;
385590593dcaSdormando         rel_time_t new_oldest = 0;
385686969ea4SBrad Fitzpatrick 
3857d9ece780STomash Brechko         set_noreply_maybe(c, tokens, ntokens);
3858d9ece780STomash Brechko 
38598c3f8d84SDustin Sallings         pthread_mutex_lock(&c->thread->stats.mutex);
3860534466e0Sdormando         c->thread->stats.flush_cmds++;
38618c3f8d84SDustin Sallings         pthread_mutex_unlock(&c->thread->stats.mutex);
3862534466e0Sdormando 
3863a2f5ca50SDaniel Pañeda         if (!settings.flush_enabled) {
3864a2f5ca50SDaniel Pañeda             // flush_all is not allowed but we log it on stats
3865a2f5ca50SDaniel Pañeda             out_string(c, "CLIENT_ERROR flush_all not allowed");
3866a2f5ca50SDaniel Pañeda             return;
3867a2f5ca50SDaniel Pañeda         }
3868a2f5ca50SDaniel Pañeda 
386990593dcaSdormando         if (ntokens != (c->noreply ? 3 : 2)) {
3870217dcce0SSteven Grimm             exptime = strtol(tokens[1].value, NULL, 10);
3871217dcce0SSteven Grimm             if(errno == ERANGE) {
3872217dcce0SSteven Grimm                 out_string(c, "CLIENT_ERROR bad command line format");
387354c9113bSElizabeth Mattijsen                 return;
387454c9113bSElizabeth Mattijsen             }
387590593dcaSdormando         }
387686969ea4SBrad Fitzpatrick 
3877d041e67dSTomash Brechko         /*
3878d041e67dSTomash Brechko           If exptime is zero realtime() would return zero too, and
3879d041e67dSTomash Brechko           realtime(exptime) - 1 would overflow to the max unsigned
3880d041e67dSTomash Brechko           value.  So we process exptime == 0 the same way we do when
3881d041e67dSTomash Brechko           no delay is given at all.
3882d041e67dSTomash Brechko         */
388390593dcaSdormando         if (exptime > 0) {
388490593dcaSdormando             new_oldest = realtime(exptime);
388590593dcaSdormando         } else { /* exptime == 0 */
388690593dcaSdormando             new_oldest = current_time;
388790593dcaSdormando         }
388890593dcaSdormando 
388990593dcaSdormando         if (settings.use_cas) {
389090593dcaSdormando             settings.oldest_live = new_oldest - 1;
389190593dcaSdormando             if (settings.oldest_live <= current_time)
389290593dcaSdormando                 settings.oldest_cas = get_cas_id();
389390593dcaSdormando         } else {
389490593dcaSdormando             settings.oldest_live = new_oldest;
389590593dcaSdormando         }
389654c9113bSElizabeth Mattijsen         out_string(c, "OK");
389754c9113bSElizabeth Mattijsen         return;
389886969ea4SBrad Fitzpatrick 
3899217dcce0SSteven Grimm     } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "version") == 0)) {
3900217dcce0SSteven Grimm 
390121c6761cSBrad Fitzpatrick         out_string(c, "VERSION " VERSION);
390286969ea4SBrad Fitzpatrick 
3903217dcce0SSteven Grimm     } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "quit") == 0)) {
3904217dcce0SSteven Grimm 
3905c9607c6dSBrad Fitzpatrick         conn_set_state(c, conn_closing);
3906217dcce0SSteven Grimm 
3907d11dc0eaSBrian Aker     } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "shutdown") == 0)) {
3908d11dc0eaSBrian Aker 
3909d11dc0eaSBrian Aker         if (settings.shutdown_command) {
3910d11dc0eaSBrian Aker             conn_set_state(c, conn_closing);
3911d11dc0eaSBrian Aker             raise(SIGINT);
3912d11dc0eaSBrian Aker         } else {
3913d11dc0eaSBrian Aker             out_string(c, "ERROR: shutdown not enabled");
3914d11dc0eaSBrian Aker         }
3915d11dc0eaSBrian Aker 
391629bfd5a1Sdormando     } else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "slabs") == 0) {
391799fc043aSdormando         if (ntokens == 5 && strcmp(tokens[COMMAND_TOKEN + 1].value, "reassign") == 0) {
391810698baeSdormando             int src, dst, rv;
391910698baeSdormando 
392010698baeSdormando             if (settings.slab_reassign == false) {
392110698baeSdormando                 out_string(c, "CLIENT_ERROR slab reassignment disabled");
392210698baeSdormando                 return;
392310698baeSdormando             }
392410698baeSdormando 
392510698baeSdormando             src = strtol(tokens[2].value, NULL, 10);
392610698baeSdormando             dst = strtol(tokens[3].value, NULL, 10);
392710698baeSdormando 
392810698baeSdormando             if (errno == ERANGE) {
392910698baeSdormando                 out_string(c, "CLIENT_ERROR bad command line format");
393010698baeSdormando                 return;
393110698baeSdormando             }
393210698baeSdormando 
393310698baeSdormando             rv = slabs_reassign(src, dst);
393410698baeSdormando             switch (rv) {
393510698baeSdormando             case REASSIGN_OK:
393610698baeSdormando                 out_string(c, "OK");
393710698baeSdormando                 break;
393810698baeSdormando             case REASSIGN_RUNNING:
39398c1c18edSdormando                 out_string(c, "BUSY currently processing reassign request");
394010698baeSdormando                 break;
394110698baeSdormando             case REASSIGN_BADCLASS:
39428c1c18edSdormando                 out_string(c, "BADCLASS invalid src or dst class id");
394310698baeSdormando                 break;
394410698baeSdormando             case REASSIGN_NOSPARE:
39458c1c18edSdormando                 out_string(c, "NOSPARE source class has no spare pages");
394610698baeSdormando                 break;
39478c1c18edSdormando             case REASSIGN_SRC_DST_SAME:
39488c1c18edSdormando                 out_string(c, "SAME src and dst class are identical");
394910698baeSdormando                 break;
395010698baeSdormando             }
395110698baeSdormando             return;
395299fc043aSdormando         } else if (ntokens == 4 &&
395399fc043aSdormando             (strcmp(tokens[COMMAND_TOKEN + 1].value, "automove") == 0)) {
395499fc043aSdormando             process_slabs_automove_command(c, tokens, ntokens);
395599fc043aSdormando         } else {
395699fc043aSdormando             out_string(c, "ERROR");
395799fc043aSdormando         }
3958d425b35bSdormando     } else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "lru_crawler") == 0) {
39596be2b6c0Sdormando         if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "crawl") == 0) {
3960e8711e1bSdormando             int rv;
39616be2b6c0Sdormando             if (settings.lru_crawler == false) {
39626be2b6c0Sdormando                 out_string(c, "CLIENT_ERROR lru crawler disabled");
39636be2b6c0Sdormando                 return;
39646be2b6c0Sdormando             }
39656be2b6c0Sdormando 
3966e8711e1bSdormando             rv = lru_crawler_crawl(tokens[2].value);
39676be2b6c0Sdormando             switch(rv) {
39686be2b6c0Sdormando             case CRAWLER_OK:
39696be2b6c0Sdormando                 out_string(c, "OK");
39706be2b6c0Sdormando                 break;
39716be2b6c0Sdormando             case CRAWLER_RUNNING:
39726be2b6c0Sdormando                 out_string(c, "BUSY currently processing crawler request");
39736be2b6c0Sdormando                 break;
39746be2b6c0Sdormando             case CRAWLER_BADCLASS:
39756be2b6c0Sdormando                 out_string(c, "BADCLASS invalid class id");
39766be2b6c0Sdormando                 break;
3977e708513aSdormando             case CRAWLER_NOTSTARTED:
3978e708513aSdormando                 out_string(c, "NOTSTARTED no items to crawl");
3979e708513aSdormando                 break;
39806be2b6c0Sdormando             }
39816be2b6c0Sdormando             return;
3982e31a5912Sdormando         } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "tocrawl") == 0) {
3983e31a5912Sdormando             uint32_t tocrawl;
3984e31a5912Sdormando              if (!safe_strtoul(tokens[2].value, &tocrawl)) {
3985e31a5912Sdormando                 out_string(c, "CLIENT_ERROR bad command line format");
3986e31a5912Sdormando                 return;
3987e31a5912Sdormando             }
3988e31a5912Sdormando             settings.lru_crawler_tocrawl = tocrawl;
3989e31a5912Sdormando             out_string(c, "OK");
3990e31a5912Sdormando             return;
399131d533f8Sdormando         } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "sleep") == 0) {
399231d533f8Sdormando             uint32_t tosleep;
399331d533f8Sdormando             if (!safe_strtoul(tokens[2].value, &tosleep)) {
399431d533f8Sdormando                 out_string(c, "CLIENT_ERROR bad command line format");
399531d533f8Sdormando                 return;
399631d533f8Sdormando             }
399731d533f8Sdormando             if (tosleep > 1000000) {
399831d533f8Sdormando                 out_string(c, "CLIENT_ERROR sleep must be one second or less");
399931d533f8Sdormando                 return;
400031d533f8Sdormando             }
400131d533f8Sdormando             settings.lru_crawler_sleep = tosleep;
400231d533f8Sdormando             out_string(c, "OK");
400331d533f8Sdormando             return;
40046be2b6c0Sdormando         } else if (ntokens == 3) {
40050d1f505cSdormando             if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "enable") == 0)) {
40060d1f505cSdormando                 if (start_item_crawler_thread() == 0) {
40070d1f505cSdormando                     out_string(c, "OK");
40080d1f505cSdormando                 } else {
40090d1f505cSdormando                     out_string(c, "ERROR failed to start lru crawler thread");
40100d1f505cSdormando                 }
40110d1f505cSdormando             } else if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "disable") == 0)) {
40120d1f505cSdormando                 if (stop_item_crawler_thread() == 0) {
40130d1f505cSdormando                     out_string(c, "OK");
40140d1f505cSdormando                 } else {
40150d1f505cSdormando                     out_string(c, "ERROR failed to stop lru crawler thread");
40160d1f505cSdormando                 }
40170d1f505cSdormando             } else {
40180d1f505cSdormando                 out_string(c, "ERROR");
40190d1f505cSdormando             }
40200d1f505cSdormando             return;
40210d1f505cSdormando         } else {
40220d1f505cSdormando             out_string(c, "ERROR");
40230d1f505cSdormando         }
4024916fff36Sdormando     } else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "watch") == 0) {
4025efa436feSdormando         process_watch_command(c, tokens, ntokens);
402631541b37Sdormando     } else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "cache_memlimit") == 0)) {
402731541b37Sdormando         process_memlimit_command(c, tokens, ntokens);
4028d9ece780STomash Brechko     } else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "verbosity") == 0)) {
4029eca55c9aSPaul Lindner         process_verbosity_command(c, tokens, ntokens);
4030217dcce0SSteven Grimm     } else {
403132f382b6SBrad Fitzpatrick         out_string(c, "ERROR");
4032217dcce0SSteven Grimm     }
403332f382b6SBrad Fitzpatrick     return;
403432f382b6SBrad Fitzpatrick }
403586969ea4SBrad Fitzpatrick 
403632f382b6SBrad Fitzpatrick /*
40377a308025SAnatoly Vorobey  * if we have a complete line in the buffer, process it.
403832f382b6SBrad Fitzpatrick  */
try_read_command(conn * c)403977dde9f9SPaul Lindner static int try_read_command(conn *c) {
404078955139STim Yardley     assert(c != NULL);
404178955139STim Yardley     assert(c->rcurr <= (c->rbuf + c->rsize));
4042f1351f9bSTrond Norbye     assert(c->rbytes > 0);
4043f1351f9bSTrond Norbye 
404415ace4b5SEric Lambert     if (c->protocol == negotiating_prot || c->transport == udp_transport)  {
4045a85a6e15STrond Norbye         if ((unsigned char)c->rbuf[0] == (unsigned char)PROTOCOL_BINARY_REQ) {
4046f1351f9bSTrond Norbye             c->protocol = binary_prot;
4047f1351f9bSTrond Norbye         } else {
4048f1351f9bSTrond Norbye             c->protocol = ascii_prot;
4049f1351f9bSTrond Norbye         }
4050f1351f9bSTrond Norbye 
4051783da601Sdormando         if (settings.verbose > 1) {
4052f1351f9bSTrond Norbye             fprintf(stderr, "%d: Client using the %s protocol\n", c->sfd,
4053f1351f9bSTrond Norbye                     prot_text(c->protocol));
4054f1351f9bSTrond Norbye         }
4055f1351f9bSTrond Norbye     }
4056f1351f9bSTrond Norbye 
4057f1351f9bSTrond Norbye     if (c->protocol == binary_prot) {
4058f1351f9bSTrond Norbye         /* Do we have the complete packet header? */
4059a85a6e15STrond Norbye         if (c->rbytes < sizeof(c->binary_header)) {
4060f1351f9bSTrond Norbye             /* need more data! */
4061f1351f9bSTrond Norbye             return 0;
4062f1351f9bSTrond Norbye         } else {
40635268604aSTrond Norbye #ifdef NEED_ALIGN
40645268604aSTrond Norbye             if (((long)(c->rcurr)) % 8 != 0) {
40655268604aSTrond Norbye                 /* must realign input buffer */
40665268604aSTrond Norbye                 memmove(c->rbuf, c->rcurr, c->rbytes);
40675268604aSTrond Norbye                 c->rcurr = c->rbuf;
4068783da601Sdormando                 if (settings.verbose > 1) {
40695268604aSTrond Norbye                     fprintf(stderr, "%d: Realign input buffer\n", c->sfd);
40705268604aSTrond Norbye                 }
40715268604aSTrond Norbye             }
40725268604aSTrond Norbye #endif
4073a85a6e15STrond Norbye             protocol_binary_request_header* req;
4074a85a6e15STrond Norbye             req = (protocol_binary_request_header*)c->rcurr;
4075a85a6e15STrond Norbye 
4076a85a6e15STrond Norbye             if (settings.verbose > 1) {
4077a85a6e15STrond Norbye                 /* Dump the packet before we convert it to host order */
4078a85a6e15STrond Norbye                 int ii;
4079a85a6e15STrond Norbye                 fprintf(stderr, "<%d Read binary protocol data:", c->sfd);
4080a85a6e15STrond Norbye                 for (ii = 0; ii < sizeof(req->bytes); ++ii) {
4081a85a6e15STrond Norbye                     if (ii % 4 == 0) {
4082a85a6e15STrond Norbye                         fprintf(stderr, "\n<%d   ", c->sfd);
4083a85a6e15STrond Norbye                     }
4084a85a6e15STrond Norbye                     fprintf(stderr, " 0x%02x", req->bytes[ii]);
4085a85a6e15STrond Norbye                 }
4086a85a6e15STrond Norbye                 fprintf(stderr, "\n");
4087f1351f9bSTrond Norbye             }
4088f1351f9bSTrond Norbye 
4089a85a6e15STrond Norbye             c->binary_header = *req;
4090a85a6e15STrond Norbye             c->binary_header.request.keylen = ntohs(req->request.keylen);
4091a85a6e15STrond Norbye             c->binary_header.request.bodylen = ntohl(req->request.bodylen);
40929791b779STrond Norbye             c->binary_header.request.cas = ntohll(req->request.cas);
4093f1351f9bSTrond Norbye 
4094a85a6e15STrond Norbye             if (c->binary_header.request.magic != PROTOCOL_BINARY_REQ) {
4095f1351f9bSTrond Norbye                 if (settings.verbose) {
4096f1351f9bSTrond Norbye                     fprintf(stderr, "Invalid magic:  %x\n",
4097a85a6e15STrond Norbye                             c->binary_header.request.magic);
4098f1351f9bSTrond Norbye                 }
4099f1351f9bSTrond Norbye                 conn_set_state(c, conn_closing);
41000a1e88daSDustin Sallings                 return -1;
4101f1351f9bSTrond Norbye             }
4102f1351f9bSTrond Norbye 
4103f1351f9bSTrond Norbye             c->msgcurr = 0;
4104f1351f9bSTrond Norbye             c->msgused = 0;
4105f1351f9bSTrond Norbye             c->iovused = 0;
4106f1351f9bSTrond Norbye             if (add_msghdr(c) != 0) {
4107e60caafaSSteven Grimm                 out_of_memory(c,
4108e60caafaSSteven Grimm                         "SERVER_ERROR Out of memory allocating headers");
4109f1351f9bSTrond Norbye                 return 0;
4110f1351f9bSTrond Norbye             }
4111f1351f9bSTrond Norbye 
4112a85a6e15STrond Norbye             c->cmd = c->binary_header.request.opcode;
4113a85a6e15STrond Norbye             c->keylen = c->binary_header.request.keylen;
4114a85a6e15STrond Norbye             c->opaque = c->binary_header.request.opaque;
4115a85a6e15STrond Norbye             /* clear the returned cas value */
4116a85a6e15STrond Norbye             c->cas = 0;
4117f1351f9bSTrond Norbye 
4118f1351f9bSTrond Norbye             dispatch_bin_command(c);
4119f1351f9bSTrond Norbye 
4120a85a6e15STrond Norbye             c->rbytes -= sizeof(c->binary_header);
4121a85a6e15STrond Norbye             c->rcurr += sizeof(c->binary_header);
4122f1351f9bSTrond Norbye         }
4123f1351f9bSTrond Norbye     } else {
4124f1351f9bSTrond Norbye         char *el, *cont;
4125217dcce0SSteven Grimm 
412677dde9f9SPaul Lindner         if (c->rbytes == 0)
412732f382b6SBrad Fitzpatrick             return 0;
412875cc8368STrond Norbye 
41297a308025SAnatoly Vorobey         el = memchr(c->rcurr, '\n', c->rbytes);
413075cc8368STrond Norbye         if (!el) {
413175cc8368STrond Norbye             if (c->rbytes > 1024) {
413275cc8368STrond Norbye                 /*
413375cc8368STrond Norbye                  * We didn't have a '\n' in the first k. This _has_ to be a
413475cc8368STrond Norbye                  * large multiget, if not we should just nuke the connection.
413575cc8368STrond Norbye                  */
413675cc8368STrond Norbye                 char *ptr = c->rcurr;
413775cc8368STrond Norbye                 while (*ptr == ' ') { /* ignore leading whitespaces */
413875cc8368STrond Norbye                     ++ptr;
413975cc8368STrond Norbye                 }
414075cc8368STrond Norbye 
4141d9cd01edSTomash Brechko                 if (ptr - c->rcurr > 100 ||
4142d9cd01edSTomash Brechko                     (strncmp(ptr, "get ", 4) && strncmp(ptr, "gets ", 5))) {
4143d9cd01edSTomash Brechko 
414475cc8368STrond Norbye                     conn_set_state(c, conn_closing);
414575cc8368STrond Norbye                     return 1;
414675cc8368STrond Norbye                 }
414775cc8368STrond Norbye             }
414875cc8368STrond Norbye 
414932f382b6SBrad Fitzpatrick             return 0;
415075cc8368STrond Norbye         }
415132f382b6SBrad Fitzpatrick         cont = el + 1;
415278955139STim Yardley         if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {
415332f382b6SBrad Fitzpatrick             el--;
415432f382b6SBrad Fitzpatrick         }
415532f382b6SBrad Fitzpatrick         *el = '\0';
415686969ea4SBrad Fitzpatrick 
415778955139STim Yardley         assert(cont <= (c->rcurr + c->rbytes));
4158217dcce0SSteven Grimm 
415970c1b5f6SSteven Grimm         c->last_cmd_time = current_time;
41607a308025SAnatoly Vorobey         process_command(c, c->rcurr);
416186969ea4SBrad Fitzpatrick 
41627a308025SAnatoly Vorobey         c->rbytes -= (cont - c->rcurr);
41637a308025SAnatoly Vorobey         c->rcurr = cont;
416486969ea4SBrad Fitzpatrick 
416578955139STim Yardley         assert(c->rcurr <= (c->rbuf + c->rsize));
4166f1351f9bSTrond Norbye     }
4167217dcce0SSteven Grimm 
416832f382b6SBrad Fitzpatrick     return 1;
416932f382b6SBrad Fitzpatrick }
417086969ea4SBrad Fitzpatrick 
417132f382b6SBrad Fitzpatrick /*
4172c9607c6dSBrad Fitzpatrick  * read a UDP request.
4173c9607c6dSBrad Fitzpatrick  */
try_read_udp(conn * c)4174496384caSSteve Yen static enum try_read_result try_read_udp(conn *c) {
4175c9607c6dSBrad Fitzpatrick     int res;
417686969ea4SBrad Fitzpatrick 
417778955139STim Yardley     assert(c != NULL);
417878955139STim Yardley 
4179c9607c6dSBrad Fitzpatrick     c->request_addr_size = sizeof(c->request_addr);
4180c0e420b9SSteven Grimm     res = recvfrom(c->sfd, c->rbuf, c->rsize,
4181c6a700a3SAlex Leone                    0, (struct sockaddr *)&c->request_addr,
4182c6a700a3SAlex Leone                    &c->request_addr_size);
4183c9607c6dSBrad Fitzpatrick     if (res > 8) {
4184c0e420b9SSteven Grimm         unsigned char *buf = (unsigned char *)c->rbuf;
4185e2da3782STrond Norbye         pthread_mutex_lock(&c->thread->stats.mutex);
4186e2da3782STrond Norbye         c->thread->stats.bytes_read += res;
4187e2da3782STrond Norbye         pthread_mutex_unlock(&c->thread->stats.mutex);
418886969ea4SBrad Fitzpatrick 
4189c9607c6dSBrad Fitzpatrick         /* Beginning of UDP packet is the request ID; save it. */
4190c9607c6dSBrad Fitzpatrick         c->request_id = buf[0] * 256 + buf[1];
419186969ea4SBrad Fitzpatrick 
4192c9607c6dSBrad Fitzpatrick         /* If this is a multi-packet request, drop it. */
4193c9607c6dSBrad Fitzpatrick         if (buf[4] != 0 || buf[5] != 1) {
4194c9607c6dSBrad Fitzpatrick             out_string(c, "SERVER_ERROR multi-packet request not supported");
4195496384caSSteve Yen             return READ_NO_DATA_RECEIVED;
4196c9607c6dSBrad Fitzpatrick         }
419786969ea4SBrad Fitzpatrick 
4198c9607c6dSBrad Fitzpatrick         /* Don't care about any of the rest of the header. */
4199c9607c6dSBrad Fitzpatrick         res -= 8;
4200c9607c6dSBrad Fitzpatrick         memmove(c->rbuf, c->rbuf + 8, res);
420186969ea4SBrad Fitzpatrick 
42022928a785Sdormando         c->rbytes = res;
4203ba0ba835SSteven Grimm         c->rcurr = c->rbuf;
4204496384caSSteve Yen         return READ_DATA_RECEIVED;
4205c9607c6dSBrad Fitzpatrick     }
4206496384caSSteve Yen     return READ_NO_DATA_RECEIVED;
4207c9607c6dSBrad Fitzpatrick }
420886969ea4SBrad Fitzpatrick 
4209c9607c6dSBrad Fitzpatrick /*
421032f382b6SBrad Fitzpatrick  * read from network as much as we can, handle buffer overflow and connection
421132f382b6SBrad Fitzpatrick  * close.
42127a308025SAnatoly Vorobey  * before reading, move the remaining incomplete fragment of a command
42137a308025SAnatoly Vorobey  * (if any) to the beginning of the buffer.
421475cc8368STrond Norbye  *
421575cc8368STrond Norbye  * To protect us from someone flooding a connection with bogus data causing
421675cc8368STrond Norbye  * the connection to eat up all available memory, break out and start looking
421775cc8368STrond Norbye  * at the data I've got after a number of reallocs...
421875cc8368STrond Norbye  *
4219496384caSSteve Yen  * @return enum try_read_result
422032f382b6SBrad Fitzpatrick  */
try_read_network(conn * c)4221496384caSSteve Yen static enum try_read_result try_read_network(conn *c) {
4222496384caSSteve Yen     enum try_read_result gotdata = READ_NO_DATA_RECEIVED;
422332f382b6SBrad Fitzpatrick     int res;
422475cc8368STrond Norbye     int num_allocs = 0;
422578955139STim Yardley     assert(c != NULL);
422678955139STim Yardley 
42277a308025SAnatoly Vorobey     if (c->rcurr != c->rbuf) {
42287a308025SAnatoly Vorobey         if (c->rbytes != 0) /* otherwise there's nothing to copy */
42297a308025SAnatoly Vorobey             memmove(c->rbuf, c->rcurr, c->rbytes);
42307a308025SAnatoly Vorobey         c->rcurr = c->rbuf;
42317a308025SAnatoly Vorobey     }
423286969ea4SBrad Fitzpatrick 
423332f382b6SBrad Fitzpatrick     while (1) {
423432f382b6SBrad Fitzpatrick         if (c->rbytes >= c->rsize) {
423575cc8368STrond Norbye             if (num_allocs == 4) {
423675cc8368STrond Norbye                 return gotdata;
423775cc8368STrond Norbye             }
423875cc8368STrond Norbye             ++num_allocs;
423932f382b6SBrad Fitzpatrick             char *new_rbuf = realloc(c->rbuf, c->rsize * 2);
424032f382b6SBrad Fitzpatrick             if (!new_rbuf) {
4241de021a9cSTrond Norbye                 STATS_LOCK();
4242de021a9cSTrond Norbye                 stats.malloc_fails++;
4243de021a9cSTrond Norbye                 STATS_UNLOCK();
4244de021a9cSTrond Norbye                 if (settings.verbose > 0) {
424532f382b6SBrad Fitzpatrick                     fprintf(stderr, "Couldn't realloc input buffer\n");
4246de021a9cSTrond Norbye                 }
424732f382b6SBrad Fitzpatrick                 c->rbytes = 0; /* ignore what we read */
4248d7757c3eSSteven Grimm                 out_of_memory(c, "SERVER_ERROR out of memory reading request");
424960d70942SAnatoly Vorobey                 c->write_and_go = conn_closing;
4250496384caSSteve Yen                 return READ_MEMORY_ERROR;
425132f382b6SBrad Fitzpatrick             }
4252204019d4STorsten Foertsch             c->rcurr = c->rbuf = new_rbuf;
4253204019d4STorsten Foertsch             c->rsize *= 2;
425432f382b6SBrad Fitzpatrick         }
425586969ea4SBrad Fitzpatrick 
42562a733a1dSTrond Norbye         int avail = c->rsize - c->rbytes;
42572a733a1dSTrond Norbye         res = read(c->sfd, c->rbuf + c->rbytes, avail);
425832f382b6SBrad Fitzpatrick         if (res > 0) {
4259e2da3782STrond Norbye             pthread_mutex_lock(&c->thread->stats.mutex);
4260e2da3782STrond Norbye             c->thread->stats.bytes_read += res;
4261e2da3782STrond Norbye             pthread_mutex_unlock(&c->thread->stats.mutex);
4262496384caSSteve Yen             gotdata = READ_DATA_RECEIVED;
426332f382b6SBrad Fitzpatrick             c->rbytes += res;
42642a733a1dSTrond Norbye             if (res == avail) {
426532f382b6SBrad Fitzpatrick                 continue;
42662a733a1dSTrond Norbye             } else {
42672a733a1dSTrond Norbye                 break;
42682a733a1dSTrond Norbye             }
426932f382b6SBrad Fitzpatrick         }
427032f382b6SBrad Fitzpatrick         if (res == 0) {
4271496384caSSteve Yen             return READ_ERROR;
427232f382b6SBrad Fitzpatrick         }
427332f382b6SBrad Fitzpatrick         if (res == -1) {
4274f1351f9bSTrond Norbye             if (errno == EAGAIN || errno == EWOULDBLOCK) {
4275f1351f9bSTrond Norbye                 break;
4276f1351f9bSTrond Norbye             }
4277496384caSSteve Yen             return READ_ERROR;
427832f382b6SBrad Fitzpatrick         }
427932f382b6SBrad Fitzpatrick     }
428032f382b6SBrad Fitzpatrick     return gotdata;
428132f382b6SBrad Fitzpatrick }
428286969ea4SBrad Fitzpatrick 
update_event(conn * c,const int new_flags)428377dde9f9SPaul Lindner static bool update_event(conn *c, const int new_flags) {
428478955139STim Yardley     assert(c != NULL);
428578955139STim Yardley 
428656b8339eSSteven Grimm     struct event_base *base = c->event.ev_base;
428732f382b6SBrad Fitzpatrick     if (c->ev_flags == new_flags)
428877dde9f9SPaul Lindner         return true;
428977dde9f9SPaul Lindner     if (event_del(&c->event) == -1) return false;
429032f382b6SBrad Fitzpatrick     event_set(&c->event, c->sfd, new_flags, event_handler, (void *)c);
429156b8339eSSteven Grimm     event_base_set(base, &c->event);
429232f382b6SBrad Fitzpatrick     c->ev_flags = new_flags;
429377dde9f9SPaul Lindner     if (event_add(&c->event, 0) == -1) return false;
429477dde9f9SPaul Lindner     return true;
429532f382b6SBrad Fitzpatrick }
429686969ea4SBrad Fitzpatrick 
4297c9607c6dSBrad Fitzpatrick /*
4298217dcce0SSteven Grimm  * Sets whether we are listening for new connections or not.
4299217dcce0SSteven Grimm  */
do_accept_new_conns(const bool do_accept)4300a0e4a756Sdormando void do_accept_new_conns(const bool do_accept) {
43019150c85bSBrian Aker     conn *next;
43029150c85bSBrian Aker 
43039150c85bSBrian Aker     for (next = listen_conn; next; next = next->next) {
4304217dcce0SSteven Grimm         if (do_accept) {
43059150c85bSBrian Aker             update_event(next, EV_READ | EV_PERSIST);
43067d010a85SChris Goffinet             if (listen(next->sfd, settings.backlog) != 0) {
4307217dcce0SSteven Grimm                 perror("listen");
4308217dcce0SSteven Grimm             }
4309217dcce0SSteven Grimm         }
4310217dcce0SSteven Grimm         else {
43119150c85bSBrian Aker             update_event(next, 0);
43129150c85bSBrian Aker             if (listen(next->sfd, 0) != 0) {
4313217dcce0SSteven Grimm                 perror("listen");
4314217dcce0SSteven Grimm             }
4315217dcce0SSteven Grimm         }
4316217dcce0SSteven Grimm     }
43173d540bdbSdormando 
43183d540bdbSdormando     if (do_accept) {
4319a1f269eeSIan Miell         struct timeval maxconns_exited;
4320a1f269eeSIan Miell         uint64_t elapsed_us;
4321a1f269eeSIan Miell         gettimeofday(&maxconns_exited,NULL);
43228d2b5b48Sdormando         STATS_LOCK();
4323a1f269eeSIan Miell         elapsed_us =
4324a1f269eeSIan Miell             (maxconns_exited.tv_sec - stats.maxconns_entered.tv_sec) * 1000000
4325a1f269eeSIan Miell             + (maxconns_exited.tv_usec - stats.maxconns_entered.tv_usec);
4326a1f269eeSIan Miell         stats.time_in_listen_disabled_us += elapsed_us;
4327cb01d504Sdormando         stats_state.accepting_conns = true;
43283d540bdbSdormando         STATS_UNLOCK();
43293d540bdbSdormando     } else {
43303d540bdbSdormando         STATS_LOCK();
4331cb01d504Sdormando         stats_state.accepting_conns = false;
4332a1f269eeSIan Miell         gettimeofday(&stats.maxconns_entered,NULL);
43333d540bdbSdormando         stats.listen_disabled_num++;
43343d540bdbSdormando         STATS_UNLOCK();
4335ac939723Sdormando         allow_new_conns = false;
43362c560909Sdormando         maxconns_handler(-42, 0, 0);
43373d540bdbSdormando     }
43389150c85bSBrian Aker }
4339217dcce0SSteven Grimm 
4340217dcce0SSteven Grimm /*
4341c9607c6dSBrad Fitzpatrick  * Transmit the next chunk of data from our list of msgbuf structures.
4342c9607c6dSBrad Fitzpatrick  *
4343c9607c6dSBrad Fitzpatrick  * Returns:
4344c9607c6dSBrad Fitzpatrick  *   TRANSMIT_COMPLETE   All done writing.
4345c9607c6dSBrad Fitzpatrick  *   TRANSMIT_INCOMPLETE More data remaining to write.
4346c9607c6dSBrad Fitzpatrick  *   TRANSMIT_SOFT_ERROR Can't write any more right now.
4347c9607c6dSBrad Fitzpatrick  *   TRANSMIT_HARD_ERROR Can't write (c->state is set to conn_closing)
4348c9607c6dSBrad Fitzpatrick  */
transmit(conn * c)4349f30083c7SSteve Yen static enum transmit_result transmit(conn *c) {
435078955139STim Yardley     assert(c != NULL);
435178955139STim Yardley 
4352c9607c6dSBrad Fitzpatrick     if (c->msgcurr < c->msgused &&
4353c9607c6dSBrad Fitzpatrick             c->msglist[c->msgcurr].msg_iovlen == 0) {
4354c9607c6dSBrad Fitzpatrick         /* Finished writing the current msg; advance to the next. */
4355c9607c6dSBrad Fitzpatrick         c->msgcurr++;
4356c9607c6dSBrad Fitzpatrick     }
4357c9607c6dSBrad Fitzpatrick     if (c->msgcurr < c->msgused) {
4358c0ec7b09SPaolo Borelli         ssize_t res;
4359c9607c6dSBrad Fitzpatrick         struct msghdr *m = &c->msglist[c->msgcurr];
4360c0ec7b09SPaolo Borelli 
4361c9607c6dSBrad Fitzpatrick         res = sendmsg(c->sfd, m, 0);
4362c9607c6dSBrad Fitzpatrick         if (res > 0) {
4363e2da3782STrond Norbye             pthread_mutex_lock(&c->thread->stats.mutex);
4364e2da3782STrond Norbye             c->thread->stats.bytes_written += res;
4365e2da3782STrond Norbye             pthread_mutex_unlock(&c->thread->stats.mutex);
436686969ea4SBrad Fitzpatrick 
4367c9607c6dSBrad Fitzpatrick             /* We've written some of the data. Remove the completed
4368c9607c6dSBrad Fitzpatrick                iovec entries from the list of pending writes. */
4369c9607c6dSBrad Fitzpatrick             while (m->msg_iovlen > 0 && res >= m->msg_iov->iov_len) {
4370c9607c6dSBrad Fitzpatrick                 res -= m->msg_iov->iov_len;
4371c9607c6dSBrad Fitzpatrick                 m->msg_iovlen--;
4372c9607c6dSBrad Fitzpatrick                 m->msg_iov++;
4373c9607c6dSBrad Fitzpatrick             }
437486969ea4SBrad Fitzpatrick 
4375c9607c6dSBrad Fitzpatrick             /* Might have written just part of the last iovec entry;
4376c9607c6dSBrad Fitzpatrick                adjust it so the next write will do the rest. */
4377c9607c6dSBrad Fitzpatrick             if (res > 0) {
4378df1b7e42STrond Norbye                 m->msg_iov->iov_base = (caddr_t)m->msg_iov->iov_base + res;
4379c9607c6dSBrad Fitzpatrick                 m->msg_iov->iov_len -= res;
4380c9607c6dSBrad Fitzpatrick             }
4381c9607c6dSBrad Fitzpatrick             return TRANSMIT_INCOMPLETE;
4382c9607c6dSBrad Fitzpatrick         }
4383c9607c6dSBrad Fitzpatrick         if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
4384c9607c6dSBrad Fitzpatrick             if (!update_event(c, EV_WRITE | EV_PERSIST)) {
4385c9607c6dSBrad Fitzpatrick                 if (settings.verbose > 0)
4386c9607c6dSBrad Fitzpatrick                     fprintf(stderr, "Couldn't update event\n");
4387c9607c6dSBrad Fitzpatrick                 conn_set_state(c, conn_closing);
4388c9607c6dSBrad Fitzpatrick                 return TRANSMIT_HARD_ERROR;
4389c9607c6dSBrad Fitzpatrick             }
4390c9607c6dSBrad Fitzpatrick             return TRANSMIT_SOFT_ERROR;
4391c9607c6dSBrad Fitzpatrick         }
4392c9607c6dSBrad Fitzpatrick         /* if res == 0 or res == -1 and error is not EAGAIN or EWOULDBLOCK,
4393c9607c6dSBrad Fitzpatrick            we have a real error, on which we close the connection */
4394c9607c6dSBrad Fitzpatrick         if (settings.verbose > 0)
4395c9607c6dSBrad Fitzpatrick             perror("Failed to write, and not due to blocking");
439686969ea4SBrad Fitzpatrick 
439715ace4b5SEric Lambert         if (IS_UDP(c->transport))
4398c9607c6dSBrad Fitzpatrick             conn_set_state(c, conn_read);
4399c9607c6dSBrad Fitzpatrick         else
4400c9607c6dSBrad Fitzpatrick             conn_set_state(c, conn_closing);
4401c9607c6dSBrad Fitzpatrick         return TRANSMIT_HARD_ERROR;
4402c9607c6dSBrad Fitzpatrick     } else {
4403c9607c6dSBrad Fitzpatrick         return TRANSMIT_COMPLETE;
4404c9607c6dSBrad Fitzpatrick     }
4405c9607c6dSBrad Fitzpatrick }
440686969ea4SBrad Fitzpatrick 
44070567967aSdormando /* Does a looped read to fill data chunks */
4408ee461d11Sdormando /* TODO: restrict number of times this can loop.
4409ee461d11Sdormando  * Also, benchmark using readv's.
4410ee461d11Sdormando  */
read_into_chunked_item(conn * c)44110567967aSdormando static int read_into_chunked_item(conn *c) {
44120567967aSdormando     int total = 0;
44130567967aSdormando     int res;
44140567967aSdormando     assert(c->rcurr != c->ritem);
44150567967aSdormando 
44160567967aSdormando     while (c->rlbytes > 0) {
44170567967aSdormando         item_chunk *ch = (item_chunk *)c->ritem;
44180567967aSdormando         int unused = ch->size - ch->used;
44190567967aSdormando         /* first check if we have leftovers in the conn_read buffer */
44200567967aSdormando         if (c->rbytes > 0) {
44210567967aSdormando             total = 0;
44220567967aSdormando             int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
44230567967aSdormando             tocopy = tocopy > unused ? unused : tocopy;
44240567967aSdormando             if (c->ritem != c->rcurr) {
44250567967aSdormando                 memmove(ch->data + ch->used, c->rcurr, tocopy);
44260567967aSdormando             }
44270567967aSdormando             total += tocopy;
44280567967aSdormando             c->rlbytes -= tocopy;
44290567967aSdormando             c->rcurr += tocopy;
44300567967aSdormando             c->rbytes -= tocopy;
44310567967aSdormando             ch->used += tocopy;
44320567967aSdormando             if (c->rlbytes == 0) {
44330567967aSdormando                 break;
44340567967aSdormando             }
44350567967aSdormando         } else {
44360567967aSdormando             /*  now try reading from the socket */
44370567967aSdormando             res = read(c->sfd, ch->data + ch->used,
44380567967aSdormando                     (unused > c->rlbytes ? c->rlbytes : unused));
44390567967aSdormando             if (res > 0) {
44400567967aSdormando                 pthread_mutex_lock(&c->thread->stats.mutex);
44410567967aSdormando                 c->thread->stats.bytes_read += res;
44420567967aSdormando                 pthread_mutex_unlock(&c->thread->stats.mutex);
44430567967aSdormando                 ch->used += res;
44440567967aSdormando                 total += res;
44450567967aSdormando                 c->rlbytes -= res;
44460567967aSdormando             } else {
44470567967aSdormando                 /* Reset total to the latest result so caller can handle it */
44480567967aSdormando                 total = res;
44490567967aSdormando                 break;
44500567967aSdormando             }
44510567967aSdormando         }
44520567967aSdormando 
44530567967aSdormando         assert(ch->used <= ch->size);
44540567967aSdormando         if (ch->size == ch->used) {
44550567967aSdormando             if (ch->next) {
44560567967aSdormando                 c->ritem = (char *) ch->next;
44570567967aSdormando             } else {
44580567967aSdormando                 /* No space left. */
44590567967aSdormando                 assert(c->rlbytes == 0);
44600567967aSdormando                 break;
44610567967aSdormando             }
44620567967aSdormando         }
44630567967aSdormando     }
44640567967aSdormando     return total;
44650567967aSdormando }
44660567967aSdormando 
drive_machine(conn * c)446777dde9f9SPaul Lindner static void drive_machine(conn *c) {
446877dde9f9SPaul Lindner     bool stop = false;
44696d02111bSSimon Liu     int sfd;
447032f382b6SBrad Fitzpatrick     socklen_t addrlen;
4471a61a6900SBrian Aker     struct sockaddr_storage addr;
4472ca90710fSdormando     int nreqs = settings.reqs_per_event;
447332f382b6SBrad Fitzpatrick     int res;
4474d1f9d992Sdormando     const char *str;
44759c9a33caSdormando #ifdef HAVE_ACCEPT4
44766d02111bSSimon Liu     static int  use_accept4 = 1;
44779c9a33caSdormando #else
44789c9a33caSdormando     static int  use_accept4 = 0;
44796d02111bSSimon Liu #endif
448086969ea4SBrad Fitzpatrick 
448178955139STim Yardley     assert(c != NULL);
448278955139STim Yardley 
4483e8371d3aSSteven Grimm     while (!stop) {
4484c0ec7b09SPaolo Borelli 
448532f382b6SBrad Fitzpatrick         switch(c->state) {
448632f382b6SBrad Fitzpatrick         case conn_listening:
448732f382b6SBrad Fitzpatrick             addrlen = sizeof(addr);
44889c9a33caSdormando #ifdef HAVE_ACCEPT4
44896d02111bSSimon Liu             if (use_accept4) {
44906d02111bSSimon Liu                 sfd = accept4(c->sfd, (struct sockaddr *)&addr, &addrlen, SOCK_NONBLOCK);
44916d02111bSSimon Liu             } else {
44926d02111bSSimon Liu                 sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen);
44936d02111bSSimon Liu             }
44946d02111bSSimon Liu #else
44956d02111bSSimon Liu             sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen);
44966d02111bSSimon Liu #endif
44976d02111bSSimon Liu             if (sfd == -1) {
44986d02111bSSimon Liu                 if (use_accept4 && errno == ENOSYS) {
44996d02111bSSimon Liu                     use_accept4 = 0;
45006d02111bSSimon Liu                     continue;
45016d02111bSSimon Liu                 }
45026d02111bSSimon Liu                 perror(use_accept4 ? "accept4()" : "accept()");
450332f382b6SBrad Fitzpatrick                 if (errno == EAGAIN || errno == EWOULDBLOCK) {
450456b8339eSSteven Grimm                     /* these are transient, so don't log anything */
450577dde9f9SPaul Lindner                     stop = true;
4506217dcce0SSteven Grimm                 } else if (errno == EMFILE) {
4507217dcce0SSteven Grimm                     if (settings.verbose > 0)
4508217dcce0SSteven Grimm                         fprintf(stderr, "Too many open connections\n");
450977dde9f9SPaul Lindner                     accept_new_conns(false);
451056b8339eSSteven Grimm                     stop = true;
451132f382b6SBrad Fitzpatrick                 } else {
451232f382b6SBrad Fitzpatrick                     perror("accept()");
451356b8339eSSteven Grimm                     stop = true;
451432f382b6SBrad Fitzpatrick                 }
451589deba77SBrad Fitzpatrick                 break;
451632f382b6SBrad Fitzpatrick             }
45176d02111bSSimon Liu             if (!use_accept4) {
45186d02111bSSimon Liu                 if (fcntl(sfd, F_SETFL, fcntl(sfd, F_GETFL) | O_NONBLOCK) < 0) {
451932f382b6SBrad Fitzpatrick                     perror("setting O_NONBLOCK");
452032f382b6SBrad Fitzpatrick                     close(sfd);
452189deba77SBrad Fitzpatrick                     break;
452232f382b6SBrad Fitzpatrick                 }
45236d02111bSSimon Liu             }
45246aafe58eSDustin Sallings 
4525d1f9d992Sdormando             if (settings.maxconns_fast &&
4526cb01d504Sdormando                 stats_state.curr_conns + stats_state.reserved_fds >= settings.maxconns - 1) {
4527d1f9d992Sdormando                 str = "ERROR Too many open connections\r\n";
4528d1f9d992Sdormando                 res = write(sfd, str, strlen(str));
4529d1f9d992Sdormando                 close(sfd);
4530d1f9d992Sdormando                 STATS_LOCK();
4531d1f9d992Sdormando                 stats.rejected_conns++;
4532d1f9d992Sdormando                 STATS_UNLOCK();
4533d1f9d992Sdormando             } else {
4534f1351f9bSTrond Norbye                 dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,
45355b847851SGuillaume Delacour                                      DATA_BUFFER_SIZE, c->transport);
4536d1f9d992Sdormando             }
4537d1f9d992Sdormando 
4538f1351f9bSTrond Norbye             stop = true;
453932f382b6SBrad Fitzpatrick             break;
454086969ea4SBrad Fitzpatrick 
4541f1351f9bSTrond Norbye         case conn_waiting:
454232f382b6SBrad Fitzpatrick             if (!update_event(c, EV_READ | EV_PERSIST)) {
45439aa4cab2SEvan Martin                 if (settings.verbose > 0)
454432f382b6SBrad Fitzpatrick                     fprintf(stderr, "Couldn't update event\n");
4545c9607c6dSBrad Fitzpatrick                 conn_set_state(c, conn_closing);
454632f382b6SBrad Fitzpatrick                 break;
454732f382b6SBrad Fitzpatrick             }
4548f1351f9bSTrond Norbye 
4549f1351f9bSTrond Norbye             conn_set_state(c, conn_read);
455077dde9f9SPaul Lindner             stop = true;
455132f382b6SBrad Fitzpatrick             break;
455286969ea4SBrad Fitzpatrick 
4553f1351f9bSTrond Norbye         case conn_read:
455415ace4b5SEric Lambert             res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);
4555ca90710fSdormando 
4556f1351f9bSTrond Norbye             switch (res) {
4557496384caSSteve Yen             case READ_NO_DATA_RECEIVED:
4558f1351f9bSTrond Norbye                 conn_set_state(c, conn_waiting);
4559f1351f9bSTrond Norbye                 break;
4560496384caSSteve Yen             case READ_DATA_RECEIVED:
4561f1351f9bSTrond Norbye                 conn_set_state(c, conn_parse_cmd);
4562f1351f9bSTrond Norbye                 break;
4563496384caSSteve Yen             case READ_ERROR:
4564f1351f9bSTrond Norbye                 conn_set_state(c, conn_closing);
4565f1351f9bSTrond Norbye                 break;
4566496384caSSteve Yen             case READ_MEMORY_ERROR: /* Failed to allocate more memory */
4567f1351f9bSTrond Norbye                 /* State already set by try_read_network */
4568f1351f9bSTrond Norbye                 break;
4569f1351f9bSTrond Norbye             }
4570f1351f9bSTrond Norbye             break;
4571f1351f9bSTrond Norbye 
4572f1351f9bSTrond Norbye         case conn_parse_cmd :
4573f1351f9bSTrond Norbye             if (try_read_command(c) == 0) {
4574f1351f9bSTrond Norbye                 /* wee need more data! */
4575f1351f9bSTrond Norbye                 conn_set_state(c, conn_waiting);
4576f1351f9bSTrond Norbye             }
4577f1351f9bSTrond Norbye 
4578f1351f9bSTrond Norbye             break;
4579f1351f9bSTrond Norbye 
4580f1351f9bSTrond Norbye         case conn_new_cmd:
4581a21f819aSTrond Norbye             /* Only process nreqs at a time to avoid starving other
4582a21f819aSTrond Norbye                connections */
4583a21f819aSTrond Norbye 
4584a21f819aSTrond Norbye             --nreqs;
4585a21f819aSTrond Norbye             if (nreqs >= 0) {
4586f1351f9bSTrond Norbye                 reset_cmd_handler(c);
4587a21f819aSTrond Norbye             } else {
4588a21f819aSTrond Norbye                 pthread_mutex_lock(&c->thread->stats.mutex);
4589a21f819aSTrond Norbye                 c->thread->stats.conn_yields++;
4590a21f819aSTrond Norbye                 pthread_mutex_unlock(&c->thread->stats.mutex);
4591a21f819aSTrond Norbye                 if (c->rbytes > 0) {
4592a21f819aSTrond Norbye                     /* We have already read in data into the input buffer,
4593a21f819aSTrond Norbye                        so libevent will most likely not signal read events
4594a21f819aSTrond Norbye                        on the socket (unless more data is available. As a
4595a21f819aSTrond Norbye                        hack we should just put in a request to write data,
4596a21f819aSTrond Norbye                        because that should be possible ;-)
4597a21f819aSTrond Norbye                     */
4598a21f819aSTrond Norbye                     if (!update_event(c, EV_WRITE | EV_PERSIST)) {
4599a21f819aSTrond Norbye                         if (settings.verbose > 0)
4600a21f819aSTrond Norbye                             fprintf(stderr, "Couldn't update event\n");
4601a21f819aSTrond Norbye                         conn_set_state(c, conn_closing);
4602b2734f83Sdormando                         break;
4603a21f819aSTrond Norbye                     }
4604a21f819aSTrond Norbye                 }
4605a21f819aSTrond Norbye                 stop = true;
4606a21f819aSTrond Norbye             }
46076aafe58eSDustin Sallings             break;
46086aafe58eSDustin Sallings 
460932f382b6SBrad Fitzpatrick         case conn_nread:
461032f382b6SBrad Fitzpatrick             if (c->rlbytes == 0) {
461132f382b6SBrad Fitzpatrick                 complete_nread(c);
461232f382b6SBrad Fitzpatrick                 break;
461332f382b6SBrad Fitzpatrick             }
46146695ccbcSHuzaifa Sidhpurwala 
46156695ccbcSHuzaifa Sidhpurwala             /* Check if rbytes < 0, to prevent crash */
46166695ccbcSHuzaifa Sidhpurwala             if (c->rlbytes < 0) {
46176695ccbcSHuzaifa Sidhpurwala                 if (settings.verbose) {
46186695ccbcSHuzaifa Sidhpurwala                     fprintf(stderr, "Invalid rlbytes to read: len %d\n", c->rlbytes);
46196695ccbcSHuzaifa Sidhpurwala                 }
46206695ccbcSHuzaifa Sidhpurwala                 conn_set_state(c, conn_closing);
46216695ccbcSHuzaifa Sidhpurwala                 break;
46226695ccbcSHuzaifa Sidhpurwala             }
4623b05653f9Sdormando 
4624b05653f9Sdormando             if (!c->item || (((item *)c->item)->it_flags & ITEM_CHUNKED) == 0) {
462532f382b6SBrad Fitzpatrick                 /* first check if we have leftovers in the conn_read buffer */
462632f382b6SBrad Fitzpatrick                 if (c->rbytes > 0) {
462732f382b6SBrad Fitzpatrick                     int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
4628a85a6e15STrond Norbye                     if (c->ritem != c->rcurr) {
4629f1351f9bSTrond Norbye                         memmove(c->ritem, c->rcurr, tocopy);
4630a85a6e15STrond Norbye                     }
46317a308025SAnatoly Vorobey                     c->ritem += tocopy;
463232f382b6SBrad Fitzpatrick                     c->rlbytes -= tocopy;
46337a308025SAnatoly Vorobey                     c->rcurr += tocopy;
463432f382b6SBrad Fitzpatrick                     c->rbytes -= tocopy;
4635f1351f9bSTrond Norbye                     if (c->rlbytes == 0) {
463632f382b6SBrad Fitzpatrick                         break;
463732f382b6SBrad Fitzpatrick                     }
4638f1351f9bSTrond Norbye                 }
463986969ea4SBrad Fitzpatrick 
464032f382b6SBrad Fitzpatrick                 /*  now try reading from the socket */
46417a308025SAnatoly Vorobey                 res = read(c->sfd, c->ritem, c->rlbytes);
464232f382b6SBrad Fitzpatrick                 if (res > 0) {
4643e2da3782STrond Norbye                     pthread_mutex_lock(&c->thread->stats.mutex);
4644e2da3782STrond Norbye                     c->thread->stats.bytes_read += res;
4645e2da3782STrond Norbye                     pthread_mutex_unlock(&c->thread->stats.mutex);
46465268604aSTrond Norbye                     if (c->rcurr == c->ritem) {
46475268604aSTrond Norbye                         c->rcurr += res;
46485268604aSTrond Norbye                     }
46497a308025SAnatoly Vorobey                     c->ritem += res;
465032f382b6SBrad Fitzpatrick                     c->rlbytes -= res;
465132f382b6SBrad Fitzpatrick                     break;
465232f382b6SBrad Fitzpatrick                 }
46530567967aSdormando             } else {
46540567967aSdormando                 res = read_into_chunked_item(c);
46550567967aSdormando                 if (res > 0)
46560567967aSdormando                     break;
46570567967aSdormando             }
46580567967aSdormando 
465932f382b6SBrad Fitzpatrick             if (res == 0) { /* end of stream */
4660c9607c6dSBrad Fitzpatrick                 conn_set_state(c, conn_closing);
466132f382b6SBrad Fitzpatrick                 break;
466232f382b6SBrad Fitzpatrick             }
46630567967aSdormando 
466432f382b6SBrad Fitzpatrick             if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
466532f382b6SBrad Fitzpatrick                 if (!update_event(c, EV_READ | EV_PERSIST)) {
46669aa4cab2SEvan Martin                     if (settings.verbose > 0)
466732f382b6SBrad Fitzpatrick                         fprintf(stderr, "Couldn't update event\n");
4668c9607c6dSBrad Fitzpatrick                     conn_set_state(c, conn_closing);
466932f382b6SBrad Fitzpatrick                     break;
467032f382b6SBrad Fitzpatrick                 }
467177dde9f9SPaul Lindner                 stop = true;
467232f382b6SBrad Fitzpatrick                 break;
467332f382b6SBrad Fitzpatrick             }
467432f382b6SBrad Fitzpatrick             /* otherwise we have a real error, on which we close the connection */
467589d5126bSTrond Norbye             if (settings.verbose > 0) {
467689d5126bSTrond Norbye                 fprintf(stderr, "Failed to read, and not due to blocking:\n"
467789d5126bSTrond Norbye                         "errno: %d %s \n"
467889d5126bSTrond Norbye                         "rcurr=%lx ritem=%lx rbuf=%lx rlbytes=%d rsize=%d\n",
467989d5126bSTrond Norbye                         errno, strerror(errno),
468089d5126bSTrond Norbye                         (long)c->rcurr, (long)c->ritem, (long)c->rbuf,
468189d5126bSTrond Norbye                         (int)c->rlbytes, (int)c->rsize);
468289d5126bSTrond Norbye             }
4683c9607c6dSBrad Fitzpatrick             conn_set_state(c, conn_closing);
468460d70942SAnatoly Vorobey             break;
468586969ea4SBrad Fitzpatrick 
468660d70942SAnatoly Vorobey         case conn_swallow:
468760d70942SAnatoly Vorobey             /* we are reading sbytes and throwing them away */
468860d70942SAnatoly Vorobey             if (c->sbytes == 0) {
4689f1351f9bSTrond Norbye                 conn_set_state(c, conn_new_cmd);
469060d70942SAnatoly Vorobey                 break;
469160d70942SAnatoly Vorobey             }
469286969ea4SBrad Fitzpatrick 
469360d70942SAnatoly Vorobey             /* first check if we have leftovers in the conn_read buffer */
469460d70942SAnatoly Vorobey             if (c->rbytes > 0) {
469560d70942SAnatoly Vorobey                 int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes;
469660d70942SAnatoly Vorobey                 c->sbytes -= tocopy;
46977a308025SAnatoly Vorobey                 c->rcurr += tocopy;
469860d70942SAnatoly Vorobey                 c->rbytes -= tocopy;
469960d70942SAnatoly Vorobey                 break;
470060d70942SAnatoly Vorobey             }
470186969ea4SBrad Fitzpatrick 
470260d70942SAnatoly Vorobey             /*  now try reading from the socket */
470360d70942SAnatoly Vorobey             res = read(c->sfd, c->rbuf, c->rsize > c->sbytes ? c->sbytes : c->rsize);
470460d70942SAnatoly Vorobey             if (res > 0) {
4705e2da3782STrond Norbye                 pthread_mutex_lock(&c->thread->stats.mutex);
4706e2da3782STrond Norbye                 c->thread->stats.bytes_read += res;
4707e2da3782STrond Norbye                 pthread_mutex_unlock(&c->thread->stats.mutex);
470860d70942SAnatoly Vorobey                 c->sbytes -= res;
470960d70942SAnatoly Vorobey                 break;
471060d70942SAnatoly Vorobey             }
471160d70942SAnatoly Vorobey             if (res == 0) { /* end of stream */
4712c9607c6dSBrad Fitzpatrick                 conn_set_state(c, conn_closing);
471360d70942SAnatoly Vorobey                 break;
471460d70942SAnatoly Vorobey             }
471560d70942SAnatoly Vorobey             if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
471660d70942SAnatoly Vorobey                 if (!update_event(c, EV_READ | EV_PERSIST)) {
47179aa4cab2SEvan Martin                     if (settings.verbose > 0)
471860d70942SAnatoly Vorobey                         fprintf(stderr, "Couldn't update event\n");
4719c9607c6dSBrad Fitzpatrick                     conn_set_state(c, conn_closing);
472060d70942SAnatoly Vorobey                     break;
472160d70942SAnatoly Vorobey                 }
472277dde9f9SPaul Lindner                 stop = true;
472360d70942SAnatoly Vorobey                 break;
472460d70942SAnatoly Vorobey             }
472560d70942SAnatoly Vorobey             /* otherwise we have a real error, on which we close the connection */
47269aa4cab2SEvan Martin             if (settings.verbose > 0)
472732f382b6SBrad Fitzpatrick                 fprintf(stderr, "Failed to read, and not due to blocking\n");
4728c9607c6dSBrad Fitzpatrick             conn_set_state(c, conn_closing);
472932f382b6SBrad Fitzpatrick             break;
473086969ea4SBrad Fitzpatrick 
473132f382b6SBrad Fitzpatrick         case conn_write:
4732c9607c6dSBrad Fitzpatrick             /*
4733c9607c6dSBrad Fitzpatrick              * We want to write out a simple response. If we haven't already,
4734c9607c6dSBrad Fitzpatrick              * assemble it into a msgbuf list (this will be a single-entry
4735c9607c6dSBrad Fitzpatrick              * list for TCP or a two-entry list for UDP).
4736c9607c6dSBrad Fitzpatrick              */
473715ace4b5SEric Lambert             if (c->iovused == 0 || (IS_UDP(c->transport) && c->iovused == 1)) {
473815ace4b5SEric Lambert                 if (add_iov(c, c->wcurr, c->wbytes) != 0) {
4739c9607c6dSBrad Fitzpatrick                     if (settings.verbose > 0)
4740c9607c6dSBrad Fitzpatrick                         fprintf(stderr, "Couldn't build response\n");
4741c9607c6dSBrad Fitzpatrick                     conn_set_state(c, conn_closing);
4742c9607c6dSBrad Fitzpatrick                     break;
4743c9607c6dSBrad Fitzpatrick                 }
4744c9607c6dSBrad Fitzpatrick             }
474586969ea4SBrad Fitzpatrick 
4746c9607c6dSBrad Fitzpatrick             /* fall through... */
474786969ea4SBrad Fitzpatrick 
4748c9607c6dSBrad Fitzpatrick         case conn_mwrite:
474915ace4b5SEric Lambert           if (IS_UDP(c->transport) && c->msgcurr == 0 && build_udp_headers(c) != 0) {
475015ace4b5SEric Lambert             if (settings.verbose > 0)
475115ace4b5SEric Lambert               fprintf(stderr, "Failed to build UDP headers\n");
475215ace4b5SEric Lambert             conn_set_state(c, conn_closing);
475315ace4b5SEric Lambert             break;
475415ace4b5SEric Lambert           }
4755c9607c6dSBrad Fitzpatrick             switch (transmit(c)) {
4756c9607c6dSBrad Fitzpatrick             case TRANSMIT_COMPLETE:
475733d37566Sdormando                 if (c->state == conn_mwrite) {
475889d0d726SSteven Grimm                     conn_release_items(c);
47596aafe58eSDustin Sallings                     /* XXX:  I don't know why this wasn't the general case */
47606aafe58eSDustin Sallings                     if(c->protocol == binary_prot) {
47616aafe58eSDustin Sallings                         conn_set_state(c, c->write_and_go);
47626aafe58eSDustin Sallings                     } else {
4763f1351f9bSTrond Norbye                         conn_set_state(c, conn_new_cmd);
47646aafe58eSDustin Sallings                     }
4765c9607c6dSBrad Fitzpatrick                 } else if (c->state == conn_write) {
476632f382b6SBrad Fitzpatrick                     if (c->write_and_free) {
476732f382b6SBrad Fitzpatrick                         free(c->write_and_free);
476832f382b6SBrad Fitzpatrick                         c->write_and_free = 0;
476932f382b6SBrad Fitzpatrick                     }
4770c9607c6dSBrad Fitzpatrick                     conn_set_state(c, c->write_and_go);
4771c9607c6dSBrad Fitzpatrick                 } else {
47729aa4cab2SEvan Martin                     if (settings.verbose > 0)
4773c9607c6dSBrad Fitzpatrick                         fprintf(stderr, "Unexpected state %d\n", c->state);
4774c9607c6dSBrad Fitzpatrick                     conn_set_state(c, conn_closing);
477532f382b6SBrad Fitzpatrick                 }
4776c9607c6dSBrad Fitzpatrick                 break;
477786969ea4SBrad Fitzpatrick 
4778c9607c6dSBrad Fitzpatrick             case TRANSMIT_INCOMPLETE:
4779c9607c6dSBrad Fitzpatrick             case TRANSMIT_HARD_ERROR:
4780c9607c6dSBrad Fitzpatrick                 break;                   /* Continue in state machine. */
478186969ea4SBrad Fitzpatrick 
4782c9607c6dSBrad Fitzpatrick             case TRANSMIT_SOFT_ERROR:
478377dde9f9SPaul Lindner                 stop = true;
478432f382b6SBrad Fitzpatrick                 break;
478532f382b6SBrad Fitzpatrick             }
478632f382b6SBrad Fitzpatrick             break;
478786969ea4SBrad Fitzpatrick 
478832f382b6SBrad Fitzpatrick         case conn_closing:
478915ace4b5SEric Lambert             if (IS_UDP(c->transport))
4790c9607c6dSBrad Fitzpatrick                 conn_cleanup(c);
4791c9607c6dSBrad Fitzpatrick             else
479232f382b6SBrad Fitzpatrick                 conn_close(c);
479377dde9f9SPaul Lindner             stop = true;
479432f382b6SBrad Fitzpatrick             break;
4795df1b7e42STrond Norbye 
479670c1b5f6SSteven Grimm         case conn_closed:
4797e73bc2e5Sdormando             /* This only happens if dormando is an idiot. */
4798e73bc2e5Sdormando             abort();
4799ee961e45Sdormando             break;
4800ee961e45Sdormando 
4801916fff36Sdormando         case conn_watch:
4802916fff36Sdormando             /* We handed off our connection to the logger thread. */
4803916fff36Sdormando             stop = true;
4804916fff36Sdormando             break;
4805df1b7e42STrond Norbye         case conn_max_state:
4806df1b7e42STrond Norbye             assert(false);
4807df1b7e42STrond Norbye             break;
480832f382b6SBrad Fitzpatrick         }
480932f382b6SBrad Fitzpatrick     }
481086969ea4SBrad Fitzpatrick 
481132f382b6SBrad Fitzpatrick     return;
481232f382b6SBrad Fitzpatrick }
481386969ea4SBrad Fitzpatrick 
event_handler(const int fd,const short which,void * arg)481477dde9f9SPaul Lindner void event_handler(const int fd, const short which, void *arg) {
481532f382b6SBrad Fitzpatrick     conn *c;
481686969ea4SBrad Fitzpatrick 
481732f382b6SBrad Fitzpatrick     c = (conn *)arg;
481878955139STim Yardley     assert(c != NULL);
481978955139STim Yardley 
482032f382b6SBrad Fitzpatrick     c->which = which;
482186969ea4SBrad Fitzpatrick 
482232f382b6SBrad Fitzpatrick     /* sanity */
482332f382b6SBrad Fitzpatrick     if (fd != c->sfd) {
48249aa4cab2SEvan Martin         if (settings.verbose > 0)
482532f382b6SBrad Fitzpatrick             fprintf(stderr, "Catastrophic: event fd doesn't match conn fd!\n");
482632f382b6SBrad Fitzpatrick         conn_close(c);
482732f382b6SBrad Fitzpatrick         return;
482832f382b6SBrad Fitzpatrick     }
482986969ea4SBrad Fitzpatrick 
483032f382b6SBrad Fitzpatrick     drive_machine(c);
483186969ea4SBrad Fitzpatrick 
483232f382b6SBrad Fitzpatrick     /* wait for next event */
483332f382b6SBrad Fitzpatrick     return;
483432f382b6SBrad Fitzpatrick }
483586969ea4SBrad Fitzpatrick 
new_socket(struct addrinfo * ai)4836a61a6900SBrian Aker static int new_socket(struct addrinfo *ai) {
483732f382b6SBrad Fitzpatrick     int sfd;
483832f382b6SBrad Fitzpatrick     int flags;
483986969ea4SBrad Fitzpatrick 
4840a61a6900SBrian Aker     if ((sfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
484132f382b6SBrad Fitzpatrick         return -1;
484232f382b6SBrad Fitzpatrick     }
484386969ea4SBrad Fitzpatrick 
484432f382b6SBrad Fitzpatrick     if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
484532f382b6SBrad Fitzpatrick         fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
484632f382b6SBrad Fitzpatrick         perror("setting O_NONBLOCK");
484732f382b6SBrad Fitzpatrick         close(sfd);
484832f382b6SBrad Fitzpatrick         return -1;
484932f382b6SBrad Fitzpatrick     }
485032f382b6SBrad Fitzpatrick     return sfd;
485132f382b6SBrad Fitzpatrick }
485286969ea4SBrad Fitzpatrick 
485386969ea4SBrad Fitzpatrick 
4854c9607c6dSBrad Fitzpatrick /*
4855c9607c6dSBrad Fitzpatrick  * Sets a socket's send buffer size to the maximum allowed by the system.
4856c9607c6dSBrad Fitzpatrick  */
maximize_sndbuf(const int sfd)485777dde9f9SPaul Lindner static void maximize_sndbuf(const int sfd) {
4858c9607c6dSBrad Fitzpatrick     socklen_t intsize = sizeof(int);
4859217dcce0SSteven Grimm     int last_good = 0;
4860c9607c6dSBrad Fitzpatrick     int min, max, avg;
4861c9607c6dSBrad Fitzpatrick     int old_size;
486286969ea4SBrad Fitzpatrick 
4863c9607c6dSBrad Fitzpatrick     /* Start with the default size. */
486477dde9f9SPaul Lindner     if (getsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &old_size, &intsize) != 0) {
4865c9607c6dSBrad Fitzpatrick         if (settings.verbose > 0)
4866c9607c6dSBrad Fitzpatrick             perror("getsockopt(SO_SNDBUF)");
4867c9607c6dSBrad Fitzpatrick         return;
4868c9607c6dSBrad Fitzpatrick     }
486986969ea4SBrad Fitzpatrick 
4870c9607c6dSBrad Fitzpatrick     /* Binary-search for the real maximum. */
4871c9607c6dSBrad Fitzpatrick     min = old_size;
4872c9607c6dSBrad Fitzpatrick     max = MAX_SENDBUF_SIZE;
487386969ea4SBrad Fitzpatrick 
4874c9607c6dSBrad Fitzpatrick     while (min <= max) {
487578955139STim Yardley         avg = ((unsigned int)(min + max)) / 2;
4876c6975ef4SPaul Lindner         if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (void *)&avg, intsize) == 0) {
4877c9607c6dSBrad Fitzpatrick             last_good = avg;
4878c9607c6dSBrad Fitzpatrick             min = avg + 1;
4879c9607c6dSBrad Fitzpatrick         } else {
4880c9607c6dSBrad Fitzpatrick             max = avg - 1;
4881c9607c6dSBrad Fitzpatrick         }
4882c9607c6dSBrad Fitzpatrick     }
488386969ea4SBrad Fitzpatrick 
4884c9607c6dSBrad Fitzpatrick     if (settings.verbose > 1)
4885c9607c6dSBrad Fitzpatrick         fprintf(stderr, "<%d send buffer was %d, now %d\n", sfd, old_size, last_good);
4886c9607c6dSBrad Fitzpatrick }
488786969ea4SBrad Fitzpatrick 
4888e337faf6STrond Norbye /**
4889e337faf6STrond Norbye  * Create a socket and bind it to a specific port number
4890c605b31fSTrond Norbye  * @param interface the interface to bind to
4891e337faf6STrond Norbye  * @param port the port number to bind to
4892e337faf6STrond Norbye  * @param transport the transport protocol (TCP / UDP)
4893e337faf6STrond Norbye  * @param portnumber_file A filepointer to write the port numbers to
4894e337faf6STrond Norbye  *        when they are successfully added to the list of ports we
4895e337faf6STrond Norbye  *        listen on.
4896e337faf6STrond Norbye  */
server_socket(const char * interface,int port,enum network_transport transport,FILE * portnumber_file)4897c605b31fSTrond Norbye static int server_socket(const char *interface,
4898c605b31fSTrond Norbye                          int port,
4899c605b31fSTrond Norbye                          enum network_transport transport,
4900e337faf6STrond Norbye                          FILE *portnumber_file) {
490132f382b6SBrad Fitzpatrick     int sfd;
490232f382b6SBrad Fitzpatrick     struct linger ling = {0, 0};
4903a61a6900SBrian Aker     struct addrinfo *ai;
49049150c85bSBrian Aker     struct addrinfo *next;
4905e337faf6STrond Norbye     struct addrinfo hints = { .ai_flags = AI_PASSIVE,
4906e337faf6STrond Norbye                               .ai_family = AF_UNSPEC };
4907a61a6900SBrian Aker     char port_buf[NI_MAXSERV];
4908a61a6900SBrian Aker     int error;
49099150c85bSBrian Aker     int success = 0;
491032f382b6SBrad Fitzpatrick     int flags =1;
491186969ea4SBrad Fitzpatrick 
491215ace4b5SEric Lambert     hints.ai_socktype = IS_UDP(transport) ? SOCK_DGRAM : SOCK_STREAM;
4913a61a6900SBrian Aker 
4914e337faf6STrond Norbye     if (port == -1) {
4915e337faf6STrond Norbye         port = 0;
4916e337faf6STrond Norbye     }
4917e337faf6STrond Norbye     snprintf(port_buf, sizeof(port_buf), "%d", port);
4918c605b31fSTrond Norbye     error= getaddrinfo(interface, port_buf, &hints, &ai);
4919a61a6900SBrian Aker     if (error != 0) {
4920a61a6900SBrian Aker         if (error != EAI_SYSTEM)
4921a61a6900SBrian Aker           fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
4922a61a6900SBrian Aker         else
4923a61a6900SBrian Aker           perror("getaddrinfo()");
49242439472aSBrian Aker         return 1;
4925a61a6900SBrian Aker     }
4926a61a6900SBrian Aker 
49272439472aSBrian Aker     for (next= ai; next; next= next->ai_next) {
49282439472aSBrian Aker         conn *listen_conn_add;
49299150c85bSBrian Aker         if ((sfd = new_socket(next)) == -1) {
49307a5a1375Sdormando             /* getaddrinfo can return "junk" addresses,
49317a5a1375Sdormando              * we make sure at least one works before erroring.
49327a5a1375Sdormando              */
4933d1f9d992Sdormando             if (errno == EMFILE) {
4934d1f9d992Sdormando                 /* ...unless we're out of fds */
4935d1f9d992Sdormando                 perror("server_socket");
4936d1f9d992Sdormando                 exit(EX_OSERR);
4937d1f9d992Sdormando             }
49387a5a1375Sdormando             continue;
493932f382b6SBrad Fitzpatrick         }
494086969ea4SBrad Fitzpatrick 
4941e3e7e658SBrian Aker #ifdef IPV6_V6ONLY
4942e3e7e658SBrian Aker         if (next->ai_family == AF_INET6) {
4943e3e7e658SBrian Aker             error = setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flags, sizeof(flags));
4944e3e7e658SBrian Aker             if (error != 0) {
4945e3e7e658SBrian Aker                 perror("setsockopt");
4946e3e7e658SBrian Aker                 close(sfd);
4947e3e7e658SBrian Aker                 continue;
4948e3e7e658SBrian Aker             }
4949e3e7e658SBrian Aker         }
4950e3e7e658SBrian Aker #endif
4951e3e7e658SBrian Aker 
4952c6975ef4SPaul Lindner         setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
495315ace4b5SEric Lambert         if (IS_UDP(transport)) {
4954c9607c6dSBrad Fitzpatrick             maximize_sndbuf(sfd);
4955c9607c6dSBrad Fitzpatrick         } else {
4956e3e7e658SBrian Aker             error = setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
4957e3e7e658SBrian Aker             if (error != 0)
4958e3e7e658SBrian Aker                 perror("setsockopt");
4959e3e7e658SBrian Aker 
4960e3e7e658SBrian Aker             error = setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
4961e3e7e658SBrian Aker             if (error != 0)
4962e3e7e658SBrian Aker                 perror("setsockopt");
4963e3e7e658SBrian Aker 
4964e3e7e658SBrian Aker             error = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
4965e3e7e658SBrian Aker             if (error != 0)
4966e3e7e658SBrian Aker                 perror("setsockopt");
4967c9607c6dSBrad Fitzpatrick         }
496886969ea4SBrad Fitzpatrick 
49699150c85bSBrian Aker         if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) {
49709150c85bSBrian Aker             if (errno != EADDRINUSE) {
497132f382b6SBrad Fitzpatrick                 perror("bind()");
497232f382b6SBrad Fitzpatrick                 close(sfd);
4973a61a6900SBrian Aker                 freeaddrinfo(ai);
49742439472aSBrian Aker                 return 1;
497532f382b6SBrad Fitzpatrick             }
49769150c85bSBrian Aker             close(sfd);
4977ace1a29aSBrian Aker             continue;
49789150c85bSBrian Aker         } else {
49799150c85bSBrian Aker             success++;
498015ace4b5SEric Lambert             if (!IS_UDP(transport) && listen(sfd, settings.backlog) == -1) {
4981c9607c6dSBrad Fitzpatrick                 perror("listen()");
4982c9607c6dSBrad Fitzpatrick                 close(sfd);
4983a61a6900SBrian Aker                 freeaddrinfo(ai);
49842439472aSBrian Aker                 return 1;
49859150c85bSBrian Aker             }
4986e337faf6STrond Norbye             if (portnumber_file != NULL &&
4987e337faf6STrond Norbye                 (next->ai_addr->sa_family == AF_INET ||
4988e337faf6STrond Norbye                  next->ai_addr->sa_family == AF_INET6)) {
4989e337faf6STrond Norbye                 union {
4990e337faf6STrond Norbye                     struct sockaddr_in in;
4991e337faf6STrond Norbye                     struct sockaddr_in6 in6;
4992e337faf6STrond Norbye                 } my_sockaddr;
4993e337faf6STrond Norbye                 socklen_t len = sizeof(my_sockaddr);
4994e337faf6STrond Norbye                 if (getsockname(sfd, (struct sockaddr*)&my_sockaddr, &len)==0) {
4995e337faf6STrond Norbye                     if (next->ai_addr->sa_family == AF_INET) {
4996e337faf6STrond Norbye                         fprintf(portnumber_file, "%s INET: %u\n",
4997e337faf6STrond Norbye                                 IS_UDP(transport) ? "UDP" : "TCP",
4998e337faf6STrond Norbye                                 ntohs(my_sockaddr.in.sin_port));
4999e337faf6STrond Norbye                     } else {
5000e337faf6STrond Norbye                         fprintf(portnumber_file, "%s INET6: %u\n",
5001e337faf6STrond Norbye                                 IS_UDP(transport) ? "UDP" : "TCP",
5002e337faf6STrond Norbye                                 ntohs(my_sockaddr.in6.sin6_port));
5003e337faf6STrond Norbye                     }
5004e337faf6STrond Norbye                 }
5005e337faf6STrond Norbye             }
50069150c85bSBrian Aker         }
50072439472aSBrian Aker 
500815ace4b5SEric Lambert         if (IS_UDP(transport)) {
50092439472aSBrian Aker             int c;
50102439472aSBrian Aker 
5011c60ca35bSTrond Norbye             for (c = 0; c < settings.num_threads_per_udp; c++) {
501270c1b5f6SSteven Grimm                 /* Allocate one UDP file descriptor per worker thread;
501370c1b5f6SSteven Grimm                  * this allows "stats conns" to separately list multiple
501470c1b5f6SSteven Grimm                  * parallel UDP requests in progress.
501570c1b5f6SSteven Grimm                  *
501670c1b5f6SSteven Grimm                  * The dispatch code round-robins new connection requests
501770c1b5f6SSteven Grimm                  * among threads, so this is guaranteed to assign one
501870c1b5f6SSteven Grimm                  * FD to each thread.
501970c1b5f6SSteven Grimm                  */
502070c1b5f6SSteven Grimm                 int per_thread_fd = c ? dup(sfd) : sfd;
502170c1b5f6SSteven Grimm                 dispatch_conn_new(per_thread_fd, conn_read,
502270c1b5f6SSteven Grimm                                   EV_READ | EV_PERSIST,
502315ace4b5SEric Lambert                                   UDP_READ_BUFFER_SIZE, transport);
50242439472aSBrian Aker             }
50252439472aSBrian Aker         } else {
50262439472aSBrian Aker             if (!(listen_conn_add = conn_new(sfd, conn_listening,
50274b2326baSDustin Sallings                                              EV_READ | EV_PERSIST, 1,
502815ace4b5SEric Lambert                                              transport, main_base))) {
50292439472aSBrian Aker                 fprintf(stderr, "failed to create listening connection\n");
50302439472aSBrian Aker                 exit(EXIT_FAILURE);
50312439472aSBrian Aker             }
50324b2326baSDustin Sallings             listen_conn_add->next = listen_conn;
50334b2326baSDustin Sallings             listen_conn = listen_conn_add;
5034c9607c6dSBrad Fitzpatrick         }
5035c9607c6dSBrad Fitzpatrick     }
5036a61a6900SBrian Aker 
5037a61a6900SBrian Aker     freeaddrinfo(ai);
5038a61a6900SBrian Aker 
50394b2326baSDustin Sallings     /* Return zero iff we detected no errors in starting up connections */
50404b2326baSDustin Sallings     return success == 0;
5041c9607c6dSBrad Fitzpatrick }
504286969ea4SBrad Fitzpatrick 
server_sockets(int port,enum network_transport transport,FILE * portnumber_file)5043c605b31fSTrond Norbye static int server_sockets(int port, enum network_transport transport,
5044c605b31fSTrond Norbye                           FILE *portnumber_file) {
5045c605b31fSTrond Norbye     if (settings.inter == NULL) {
5046c605b31fSTrond Norbye         return server_socket(settings.inter, port, transport, portnumber_file);
5047c605b31fSTrond Norbye     } else {
5048c605b31fSTrond Norbye         // tokenize them and bind to each one of them..
5049c605b31fSTrond Norbye         char *b;
5050c605b31fSTrond Norbye         int ret = 0;
5051c605b31fSTrond Norbye         char *list = strdup(settings.inter);
5052c605b31fSTrond Norbye 
5053c605b31fSTrond Norbye         if (list == NULL) {
5054c605b31fSTrond Norbye             fprintf(stderr, "Failed to allocate memory for parsing server interface string\n");
5055c605b31fSTrond Norbye             return 1;
5056c605b31fSTrond Norbye         }
5057c605b31fSTrond Norbye         for (char *p = strtok_r(list, ";,", &b);
5058c605b31fSTrond Norbye              p != NULL;
5059c605b31fSTrond Norbye              p = strtok_r(NULL, ";,", &b)) {
50606ba9aa27STrond Norbye             int the_port = port;
50614baa578fSgithublvv 
50624baa578fSgithublvv             char *h = NULL;
50634baa578fSgithublvv             if (*p == '[') {
50644baa578fSgithublvv                 // expecting it to be an IPv6 address enclosed in []
50654baa578fSgithublvv                 // i.e. RFC3986 style recommended by RFC5952
50664baa578fSgithublvv                 char *e = strchr(p, ']');
50674baa578fSgithublvv                 if (e == NULL) {
50684baa578fSgithublvv                     fprintf(stderr, "Invalid IPV6 address: \"%s\"", p);
50694baa578fSgithublvv                     return 1;
50704baa578fSgithublvv                 }
50714baa578fSgithublvv                 h = ++p; // skip the opening '['
50724baa578fSgithublvv                 *e = '\0';
50734baa578fSgithublvv                 p = ++e; // skip the closing ']'
50744baa578fSgithublvv             }
50754baa578fSgithublvv 
50766ba9aa27STrond Norbye             char *s = strchr(p, ':');
50776ba9aa27STrond Norbye             if (s != NULL) {
50784baa578fSgithublvv                 // If no more semicolons - attempt to treat as port number.
50794baa578fSgithublvv                 // Otherwise the only valid option is an unenclosed IPv6 without port, until
50804baa578fSgithublvv                 // of course there was an RFC3986 IPv6 address previously specified -
50814baa578fSgithublvv                 // in such a case there is no good option, will just send it to fail as port number.
50824baa578fSgithublvv                 if (strchr(s + 1, ':') == NULL || h != NULL) {
50836ba9aa27STrond Norbye                     *s = '\0';
50846ba9aa27STrond Norbye                     ++s;
50856ba9aa27STrond Norbye                     if (!safe_strtol(s, &the_port)) {
50866ba9aa27STrond Norbye                         fprintf(stderr, "Invalid port number: \"%s\"", s);
50876ba9aa27STrond Norbye                         return 1;
50886ba9aa27STrond Norbye                     }
50896ba9aa27STrond Norbye                 }
50904baa578fSgithublvv             }
50914baa578fSgithublvv 
50924baa578fSgithublvv             if (h != NULL)
50934baa578fSgithublvv                 p = h;
50944baa578fSgithublvv 
50956ba9aa27STrond Norbye             if (strcmp(p, "*") == 0) {
50966ba9aa27STrond Norbye                 p = NULL;
50976ba9aa27STrond Norbye             }
50986ba9aa27STrond Norbye             ret |= server_socket(p, the_port, transport, portnumber_file);
5099c605b31fSTrond Norbye         }
5100c605b31fSTrond Norbye         free(list);
5101c605b31fSTrond Norbye         return ret;
5102c605b31fSTrond Norbye     }
5103c605b31fSTrond Norbye }
5104c605b31fSTrond Norbye 
new_socket_unix(void)510577dde9f9SPaul Lindner static int new_socket_unix(void) {
5106c9607c6dSBrad Fitzpatrick     int sfd;
5107c9607c6dSBrad Fitzpatrick     int flags;
510886969ea4SBrad Fitzpatrick 
5109c9607c6dSBrad Fitzpatrick     if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
5110c9607c6dSBrad Fitzpatrick         perror("socket()");
5111c9607c6dSBrad Fitzpatrick         return -1;
5112c9607c6dSBrad Fitzpatrick     }
511386969ea4SBrad Fitzpatrick 
5114c9607c6dSBrad Fitzpatrick     if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
5115c9607c6dSBrad Fitzpatrick         fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
5116c9607c6dSBrad Fitzpatrick         perror("setting O_NONBLOCK");
5117c9607c6dSBrad Fitzpatrick         close(sfd);
5118c9607c6dSBrad Fitzpatrick         return -1;
5119c9607c6dSBrad Fitzpatrick     }
5120c9607c6dSBrad Fitzpatrick     return sfd;
5121c9607c6dSBrad Fitzpatrick }
512286969ea4SBrad Fitzpatrick 
server_socket_unix(const char * path,int access_mask)51232439472aSBrian Aker static int server_socket_unix(const char *path, int access_mask) {
5124c9607c6dSBrad Fitzpatrick     int sfd;
5125c9607c6dSBrad Fitzpatrick     struct linger ling = {0, 0};
5126c9607c6dSBrad Fitzpatrick     struct sockaddr_un addr;
5127c9607c6dSBrad Fitzpatrick     struct stat tstat;
5128c9607c6dSBrad Fitzpatrick     int flags =1;
512940c76cedSDavid Bremner     int old_umask;
513086969ea4SBrad Fitzpatrick 
5131c9607c6dSBrad Fitzpatrick     if (!path) {
51322439472aSBrian Aker         return 1;
5133c9607c6dSBrad Fitzpatrick     }
513486969ea4SBrad Fitzpatrick 
5135c9607c6dSBrad Fitzpatrick     if ((sfd = new_socket_unix()) == -1) {
51362439472aSBrian Aker         return 1;
5137c9607c6dSBrad Fitzpatrick     }
513886969ea4SBrad Fitzpatrick 
5139c9607c6dSBrad Fitzpatrick     /*
5140c9607c6dSBrad Fitzpatrick      * Clean up a previous socket file if we left it around
5141c9607c6dSBrad Fitzpatrick      */
514277dde9f9SPaul Lindner     if (lstat(path, &tstat) == 0) {
5143c9607c6dSBrad Fitzpatrick         if (S_ISSOCK(tstat.st_mode))
5144c9607c6dSBrad Fitzpatrick             unlink(path);
5145c9607c6dSBrad Fitzpatrick     }
514686969ea4SBrad Fitzpatrick 
5147c6975ef4SPaul Lindner     setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
5148c6975ef4SPaul Lindner     setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
5149c6975ef4SPaul Lindner     setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
515086969ea4SBrad Fitzpatrick 
5151c9607c6dSBrad Fitzpatrick     /*
5152c9607c6dSBrad Fitzpatrick      * the memset call clears nonstandard fields in some impementations
5153c9607c6dSBrad Fitzpatrick      * that otherwise mess things up.
5154c9607c6dSBrad Fitzpatrick      */
5155c9607c6dSBrad Fitzpatrick     memset(&addr, 0, sizeof(addr));
515686969ea4SBrad Fitzpatrick 
5157c9607c6dSBrad Fitzpatrick     addr.sun_family = AF_UNIX;
51585f95d055SDustin Sallings     strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
51595f95d055SDustin Sallings     assert(strcmp(addr.sun_path, path) == 0);
516040c76cedSDavid Bremner     old_umask = umask( ~(access_mask&0777));
5161c9607c6dSBrad Fitzpatrick     if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
5162c9607c6dSBrad Fitzpatrick         perror("bind()");
5163c9607c6dSBrad Fitzpatrick         close(sfd);
516440c76cedSDavid Bremner         umask(old_umask);
51652439472aSBrian Aker         return 1;
5166c9607c6dSBrad Fitzpatrick     }
516740c76cedSDavid Bremner     umask(old_umask);
51687d010a85SChris Goffinet     if (listen(sfd, settings.backlog) == -1) {
516932f382b6SBrad Fitzpatrick         perror("listen()");
517032f382b6SBrad Fitzpatrick         close(sfd);
51712439472aSBrian Aker         return 1;
51722439472aSBrian Aker     }
51732439472aSBrian Aker     if (!(listen_conn = conn_new(sfd, conn_listening,
5174999bbb17Sdormando                                  EV_READ | EV_PERSIST, 1,
517515ace4b5SEric Lambert                                  local_transport, main_base))) {
51762439472aSBrian Aker         fprintf(stderr, "failed to create listening connection\n");
51772439472aSBrian Aker         exit(EXIT_FAILURE);
517832f382b6SBrad Fitzpatrick     }
517986969ea4SBrad Fitzpatrick 
51802439472aSBrian Aker     return 0;
5181c08383afSBrad Fitzpatrick }
518286969ea4SBrad Fitzpatrick 
5183c9607c6dSBrad Fitzpatrick /*
5184c9607c6dSBrad Fitzpatrick  * We keep the current time of day in a global variable that's updated by a
5185c9607c6dSBrad Fitzpatrick  * timer event. This saves us a bunch of time() system calls (we really only
5186c9607c6dSBrad Fitzpatrick  * need to get the time once a second, whereas there can be tens of thousands
5187c9607c6dSBrad Fitzpatrick  * of requests a second) and allows us to use server-start-relative timestamps
5188c9607c6dSBrad Fitzpatrick  * rather than absolute UNIX timestamps, a space savings on systems where
5189c9607c6dSBrad Fitzpatrick  * sizeof(time_t) > sizeof(unsigned int).
5190c9607c6dSBrad Fitzpatrick  */
5191c9607c6dSBrad Fitzpatrick volatile rel_time_t current_time;
519277dde9f9SPaul Lindner static struct event clockevent;
519386969ea4SBrad Fitzpatrick 
519451b68daaSdormando /* libevent uses a monotonic clock when available for event scheduling. Aside
519551b68daaSdormando  * from jitter, simply ticking our internal timer here is accurate enough.
519651b68daaSdormando  * Note that users who are setting explicit dates for expiration times *must*
519751b68daaSdormando  * ensure their clocks are correct before starting memcached. */
clock_handler(const int fd,const short which,void * arg)519877dde9f9SPaul Lindner static void clock_handler(const int fd, const short which, void *arg) {
519977dde9f9SPaul Lindner     struct timeval t = {.tv_sec = 1, .tv_usec = 0};
520077dde9f9SPaul Lindner     static bool initialized = false;
5201569decdbSdormando #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
5202569decdbSdormando     static bool monotonic = false;
5203569decdbSdormando     static time_t monotonic_start;
5204569decdbSdormando #endif
520586969ea4SBrad Fitzpatrick 
5206c9607c6dSBrad Fitzpatrick     if (initialized) {
5207c9607c6dSBrad Fitzpatrick         /* only delete the event if it's actually there. */
5208c9607c6dSBrad Fitzpatrick         evtimer_del(&clockevent);
5209c9607c6dSBrad Fitzpatrick     } else {
521077dde9f9SPaul Lindner         initialized = true;
521151b68daaSdormando         /* process_started is initialized to time() - 2. We initialize to 1 so
521251b68daaSdormando          * flush_all won't underflow during tests. */
5213569decdbSdormando #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
5214569decdbSdormando         struct timespec ts;
5215569decdbSdormando         if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
5216569decdbSdormando             monotonic = true;
5217d7324b0bSdormando             monotonic_start = ts.tv_sec - ITEM_UPDATE_INTERVAL - 2;
5218569decdbSdormando         }
5219569decdbSdormando #endif
5220c9607c6dSBrad Fitzpatrick     }
522186969ea4SBrad Fitzpatrick 
5222c9607c6dSBrad Fitzpatrick     evtimer_set(&clockevent, clock_handler, 0);
522356b8339eSSteven Grimm     event_base_set(main_base, &clockevent);
5224c9607c6dSBrad Fitzpatrick     evtimer_add(&clockevent, &t);
522586969ea4SBrad Fitzpatrick 
5226569decdbSdormando #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
5227569decdbSdormando     if (monotonic) {
5228569decdbSdormando         struct timespec ts;
5229569decdbSdormando         if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
5230569decdbSdormando             return;
5231569decdbSdormando         current_time = (rel_time_t) (ts.tv_sec - monotonic_start);
5232569decdbSdormando         return;
5233569decdbSdormando     }
5234569decdbSdormando #endif
5235569decdbSdormando     {
5236569decdbSdormando         struct timeval tv;
5237569decdbSdormando         gettimeofday(&tv, NULL);
5238569decdbSdormando         current_time = (rel_time_t) (tv.tv_sec - process_started);
5239569decdbSdormando     }
5240c9607c6dSBrad Fitzpatrick }
524186969ea4SBrad Fitzpatrick 
usage(void)524277dde9f9SPaul Lindner static void usage(void) {
52430c3b47f4SBrad Fitzpatrick     printf(PACKAGE " " VERSION "\n");
524410862f60SPaul Lindner     printf("-p <num>      TCP port number to listen on (default: 11211)\n"
5245b3723633SBrian Aker            "-U <num>      UDP port number to listen on (default: 11211, 0 is off)\n"
5246223a4364SMatt Ingenthron            "-s <file>     UNIX socket path to listen on (disables network support)\n"
5247d11dc0eaSBrian Aker            "-A            enable ascii \"shutdown\" command\n"
5248223a4364SMatt Ingenthron            "-a <mask>     access mask for UNIX socket, in octal (default: 0700)\n"
52496ba9aa27STrond Norbye            "-l <addr>     interface to listen on (default: INADDR_ANY, all addresses)\n"
52506ba9aa27STrond Norbye            "              <addr> may be specified as host:port. If you don't specify\n"
52516ba9aa27STrond Norbye            "              a port number, the value you specified with -p or -U is\n"
52526ba9aa27STrond Norbye            "              used. You may specify multiple addresses separated by comma\n"
52536ba9aa27STrond Norbye            "              or by using -l multiple times\n"
52546ba9aa27STrond Norbye 
525510862f60SPaul Lindner            "-d            run as a daemon\n"
525610862f60SPaul Lindner            "-r            maximize core file limit\n"
525710862f60SPaul Lindner            "-u <username> assume identity of <username> (only when run as root)\n"
5258223a4364SMatt Ingenthron            "-m <num>      max memory to use for items in megabytes (default: 64 MB)\n"
525910862f60SPaul Lindner            "-M            return error on memory exhausted (rather than removing items)\n"
5260223a4364SMatt Ingenthron            "-c <num>      max simultaneous connections (default: 1024)\n"
526172a2eb4dSTomash Brechko            "-k            lock down all paged memory.  Note that there is a\n"
526272a2eb4dSTomash Brechko            "              limit on how much memory you may lock.  Trying to\n"
526372a2eb4dSTomash Brechko            "              allocate more than that would fail, so be sure you\n"
526472a2eb4dSTomash Brechko            "              set the limit correctly for the user you started\n"
526572a2eb4dSTomash Brechko            "              the daemon with (not for -u <username> user;\n"
526672a2eb4dSTomash Brechko            "              under sh this is done with 'ulimit -S -l NUM_KB').\n"
526710862f60SPaul Lindner            "-v            verbose (print errors/warnings while in event loop)\n"
526810862f60SPaul Lindner            "-vv           very verbose (also print client commands/reponses)\n"
5269f1351f9bSTrond Norbye            "-vvv          extremely verbose (also print internal state transitions)\n"
527010862f60SPaul Lindner            "-h            print this help and exit\n"
527110862f60SPaul Lindner            "-i            print memcached and libevent license\n"
5272dda51c02SDan McGee            "-V            print version and exit\n"
527310862f60SPaul Lindner            "-P <file>     save PID in <file>, only used with -d option\n"
5274223a4364SMatt Ingenthron            "-f <factor>   chunk size growth factor (default: 1.25)\n"
5275a772ec24SVladimir            "-n <bytes>    minimum space allocated for key+value+flags (default: 48)\n");
5276a772ec24SVladimir     printf("-L            Try to use large memory pages (if available). Increasing\n"
5277a6b35b44STrond Norbye            "              the memory page size could reduce the number of TLB misses\n"
5278a6b35b44STrond Norbye            "              and improve the performance. In order to get large pages\n"
5279a6b35b44STrond Norbye            "              from the OS, memcached will allocate the total item-cache\n"
5280a772ec24SVladimir            "              in one large chunk.\n");
528141e1b0c4STrond Norbye     printf("-D <char>     Use <char> as the delimiter between key prefixes and IDs.\n"
528241e1b0c4STrond Norbye            "              This is used for per-prefix stats reporting. The default is\n"
528341e1b0c4STrond Norbye            "              \":\" (colon). If this option is specified, stats collection\n"
528441e1b0c4STrond Norbye            "              is turned on automatically; if not, then it may be turned on\n"
528541e1b0c4STrond Norbye            "              by sending the \"stats detail on\" command to the server.\n");
5286223a4364SMatt Ingenthron     printf("-t <num>      number of threads to use (default: 4)\n");
5287223a4364SMatt Ingenthron     printf("-R            Maximum number of requests per event, limits the number of\n"
5288223a4364SMatt Ingenthron            "              requests process for a given connection to prevent \n"
5289223a4364SMatt Ingenthron            "              starvation (default: 20)\n");
5290eda68b70STrond Norbye     printf("-C            Disable use of CAS\n");
5291e91d8e48SMiroslav Lichvar     printf("-b <num>      Set the backlog queue limit (default: 1024)\n");
5292a155b044SDustin Sallings     printf("-B            Binding protocol - one of ascii, binary, or auto (default)\n");
5293a75ce62aSdormando     printf("-I            Override the size of each slab page. Adjusts max item size\n"
5294a75ce62aSdormando            "              (default: 1mb, min: 1k, max: 128m)\n");
5295f1307c4dSDustin Sallings #ifdef ENABLE_SASL
5296f1307c4dSDustin Sallings     printf("-S            Turn on Sasl authentication\n");
5297f1307c4dSDustin Sallings #endif
5298a2f5ca50SDaniel Pañeda     printf("-F            Disable flush_all command\n");
5299d1f9d992Sdormando     printf("-o            Comma separated list of extended or experimental options\n"
53003cee7d1bSdormando            "              - maxconns_fast: immediately close new\n"
53011db1de38Sdormando            "                connections if over maxconns limit\n"
53021db1de38Sdormando            "              - hashpower: An integer multiplier for how large the hash\n"
53031db1de38Sdormando            "                table should be. Can be grown at runtime if not big enough.\n"
53041db1de38Sdormando            "                Set this based on \"STAT hash_power_level\" before a \n"
53051db1de38Sdormando            "                restart.\n"
5306058af0d8SKeyur            "              - tail_repair_time: Time in seconds that indicates how long to wait before\n"
5307058af0d8SKeyur            "                forcefully taking over the LRU tail item whose refcount has leaked.\n"
530809e15d5cSdormando            "                Disabled by default; dangerous option.\n"
530905ca809cSdormando            "              - hash_algorithm: The hash table algorithm\n"
531005ca809cSdormando            "                default is jenkins hash. options: jenkins, murmur3\n"
531167ac1092Sdormando            "              - lru_crawler: Enable LRU Crawler background thread\n"
531267ac1092Sdormando            "              - lru_crawler_sleep: Microseconds to sleep between items\n"
531367ac1092Sdormando            "                default is 100.\n"
5314e31a5912Sdormando            "              - lru_crawler_tocrawl: Max items to crawl per slab per run\n"
5315e31a5912Sdormando            "                default is 0 (unlimited)\n"
531609e15d5cSdormando            "              - lru_maintainer: Enable new LRU system + background thread\n"
531709e15d5cSdormando            "              - hot_lru_pct: Pct of slab memory to reserve for hot lru.\n"
531809e15d5cSdormando            "                (requires lru_maintainer)\n"
531909e15d5cSdormando            "              - warm_lru_pct: Pct of slab memory to reserve for warm lru.\n"
532009e15d5cSdormando            "                (requires lru_maintainer)\n"
532109e15d5cSdormando            "              - expirezero_does_not_evict: Items set to not expire, will not evict.\n"
532209e15d5cSdormando            "                (requires lru_maintainer)\n"
532383ba6bd9SJay Grizzard            "              - idle_timeout: Timeout for idle connections\n"
53243cee7d1bSdormando            "              - (EXPERIMENTAL) slab_chunk_max: Maximum slab size. Do not change without extreme care.\n"
5325d704f2c0Sdormando            "              - watcher_logbuf_size: Size in kilobytes of per-watcher write buffer.\n"
5326d704f2c0Sdormando            "              - worker_logbuf_Size: Size in kilobytes of per-worker-thread buffer\n"
5327d704f2c0Sdormando            "                read by background thread. Which is then written to watchers.\n"
5328cad0ecbfSdormando            "              - modern: Enables 'modern' defaults. See release notes (higly recommended!).\n"
53298d82383fSdormando            "              - track_sizes: Enable dynamic reports for 'stats sizes' command.\n"
53301db1de38Sdormando            );
53310c3b47f4SBrad Fitzpatrick     return;
53320c3b47f4SBrad Fitzpatrick }
533386969ea4SBrad Fitzpatrick 
usage_license(void)533477dde9f9SPaul Lindner static void usage_license(void) {
53350c3b47f4SBrad Fitzpatrick     printf(PACKAGE " " VERSION "\n\n");
53360c3b47f4SBrad Fitzpatrick     printf(
53370c3b47f4SBrad Fitzpatrick     "Copyright (c) 2003, Danga Interactive, Inc. <http://www.danga.com/>\n"
53380c3b47f4SBrad Fitzpatrick     "All rights reserved.\n"
53390c3b47f4SBrad Fitzpatrick     "\n"
53400c3b47f4SBrad Fitzpatrick     "Redistribution and use in source and binary forms, with or without\n"
53410c3b47f4SBrad Fitzpatrick     "modification, are permitted provided that the following conditions are\n"
53420c3b47f4SBrad Fitzpatrick     "met:\n"
53430c3b47f4SBrad Fitzpatrick     "\n"
53440c3b47f4SBrad Fitzpatrick     "    * Redistributions of source code must retain the above copyright\n"
53450c3b47f4SBrad Fitzpatrick     "notice, this list of conditions and the following disclaimer.\n"
53460c3b47f4SBrad Fitzpatrick     "\n"
53470c3b47f4SBrad Fitzpatrick     "    * Redistributions in binary form must reproduce the above\n"
53480c3b47f4SBrad Fitzpatrick     "copyright notice, this list of conditions and the following disclaimer\n"
53490c3b47f4SBrad Fitzpatrick     "in the documentation and/or other materials provided with the\n"
53500c3b47f4SBrad Fitzpatrick     "distribution.\n"
53510c3b47f4SBrad Fitzpatrick     "\n"
53520c3b47f4SBrad Fitzpatrick     "    * Neither the name of the Danga Interactive nor the names of its\n"
53530c3b47f4SBrad Fitzpatrick     "contributors may be used to endorse or promote products derived from\n"
53540c3b47f4SBrad Fitzpatrick     "this software without specific prior written permission.\n"
53550c3b47f4SBrad Fitzpatrick     "\n"
53560c3b47f4SBrad Fitzpatrick     "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
53570c3b47f4SBrad Fitzpatrick     "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
53580c3b47f4SBrad Fitzpatrick     "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
53590c3b47f4SBrad Fitzpatrick     "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
53600c3b47f4SBrad Fitzpatrick     "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
53610c3b47f4SBrad Fitzpatrick     "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
53620c3b47f4SBrad Fitzpatrick     "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
53630c3b47f4SBrad Fitzpatrick     "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
53640c3b47f4SBrad Fitzpatrick     "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
53650c3b47f4SBrad Fitzpatrick     "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
53660c3b47f4SBrad Fitzpatrick     "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
53670c3b47f4SBrad Fitzpatrick     "\n"
53680c3b47f4SBrad Fitzpatrick     "\n"
53690c3b47f4SBrad Fitzpatrick     "This product includes software developed by Niels Provos.\n"
53700c3b47f4SBrad Fitzpatrick     "\n"
53710c3b47f4SBrad Fitzpatrick     "[ libevent ]\n"
53720c3b47f4SBrad Fitzpatrick     "\n"
53730c3b47f4SBrad Fitzpatrick     "Copyright 2000-2003 Niels Provos <[email protected]>\n"
53740c3b47f4SBrad Fitzpatrick     "All rights reserved.\n"
53750c3b47f4SBrad Fitzpatrick     "\n"
53760c3b47f4SBrad Fitzpatrick     "Redistribution and use in source and binary forms, with or without\n"
53770c3b47f4SBrad Fitzpatrick     "modification, are permitted provided that the following conditions\n"
53780c3b47f4SBrad Fitzpatrick     "are met:\n"
53790c3b47f4SBrad Fitzpatrick     "1. Redistributions of source code must retain the above copyright\n"
53800c3b47f4SBrad Fitzpatrick     "   notice, this list of conditions and the following disclaimer.\n"
53810c3b47f4SBrad Fitzpatrick     "2. Redistributions in binary form must reproduce the above copyright\n"
53820c3b47f4SBrad Fitzpatrick     "   notice, this list of conditions and the following disclaimer in the\n"
53830c3b47f4SBrad Fitzpatrick     "   documentation and/or other materials provided with the distribution.\n"
53840c3b47f4SBrad Fitzpatrick     "3. All advertising materials mentioning features or use of this software\n"
53850c3b47f4SBrad Fitzpatrick     "   must display the following acknowledgement:\n"
53860c3b47f4SBrad Fitzpatrick     "      This product includes software developed by Niels Provos.\n"
53870c3b47f4SBrad Fitzpatrick     "4. The name of the author may not be used to endorse or promote products\n"
53880c3b47f4SBrad Fitzpatrick     "   derived from this software without specific prior written permission.\n"
53890c3b47f4SBrad Fitzpatrick     "\n"
53900c3b47f4SBrad Fitzpatrick     "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
53910c3b47f4SBrad Fitzpatrick     "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
53920c3b47f4SBrad Fitzpatrick     "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
53930c3b47f4SBrad Fitzpatrick     "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
53940c3b47f4SBrad Fitzpatrick     "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n"
53950c3b47f4SBrad Fitzpatrick     "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
53960c3b47f4SBrad Fitzpatrick     "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
53970c3b47f4SBrad Fitzpatrick     "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
53980c3b47f4SBrad Fitzpatrick     "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n"
53990c3b47f4SBrad Fitzpatrick     "THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
54000c3b47f4SBrad Fitzpatrick     );
540186969ea4SBrad Fitzpatrick 
540232f382b6SBrad Fitzpatrick     return;
540332f382b6SBrad Fitzpatrick }
540486969ea4SBrad Fitzpatrick 
save_pid(const char * pid_file)5405af48b100STrond Norbye static void save_pid(const char *pid_file) {
5406032912ceSLisa Seelye     FILE *fp;
5407af48b100STrond Norbye     if (access(pid_file, F_OK) == 0) {
5408af48b100STrond Norbye         if ((fp = fopen(pid_file, "r")) != NULL) {
5409af48b100STrond Norbye             char buffer[1024];
5410af48b100STrond Norbye             if (fgets(buffer, sizeof(buffer), fp) != NULL) {
5411af48b100STrond Norbye                 unsigned int pid;
5412af48b100STrond Norbye                 if (safe_strtoul(buffer, &pid) && kill((pid_t)pid, 0) == 0) {
5413af48b100STrond Norbye                     fprintf(stderr, "WARNING: The pid file contained the following (running) pid: %u\n", pid);
5414af48b100STrond Norbye                 }
5415af48b100STrond Norbye             }
5416af48b100STrond Norbye             fclose(fp);
5417af48b100STrond Norbye         }
5418af48b100STrond Norbye     }
541986969ea4SBrad Fitzpatrick 
54208ae10ebaSKenneth Steele     /* Create the pid file first with a temporary name, then
54218ae10ebaSKenneth Steele      * atomically move the file to the real name to avoid a race with
54228ae10ebaSKenneth Steele      * another process opening the file to read the pid, but finding
54238ae10ebaSKenneth Steele      * it empty.
54248ae10ebaSKenneth Steele      */
54258ae10ebaSKenneth Steele     char tmp_pid_file[1024];
54268ae10ebaSKenneth Steele     snprintf(tmp_pid_file, sizeof(tmp_pid_file), "%s.tmp", pid_file);
54278ae10ebaSKenneth Steele 
54288ae10ebaSKenneth Steele     if ((fp = fopen(tmp_pid_file, "w")) == NULL) {
54298ae10ebaSKenneth Steele         vperror("Could not open the pid file %s for writing", tmp_pid_file);
5430032912ceSLisa Seelye         return;
5431032912ceSLisa Seelye     }
543286969ea4SBrad Fitzpatrick 
5433af48b100STrond Norbye     fprintf(fp,"%ld\n", (long)getpid());
5434032912ceSLisa Seelye     if (fclose(fp) == -1) {
54358ae10ebaSKenneth Steele         vperror("Could not close the pid file %s", tmp_pid_file);
54368ae10ebaSKenneth Steele     }
54378ae10ebaSKenneth Steele 
54388ae10ebaSKenneth Steele     if (rename(tmp_pid_file, pid_file) != 0) {
54398ae10ebaSKenneth Steele         vperror("Could not rename the pid file from %s to %s",
54408ae10ebaSKenneth Steele                 tmp_pid_file, pid_file);
5441032912ceSLisa Seelye     }
5442032912ceSLisa Seelye }
544386969ea4SBrad Fitzpatrick 
remove_pidfile(const char * pid_file)544477dde9f9SPaul Lindner static void remove_pidfile(const char *pid_file) {
54453f2f8c70SPaul Lindner   if (pid_file == NULL)
5446032912ceSLisa Seelye       return;
544786969ea4SBrad Fitzpatrick 
544877dde9f9SPaul Lindner   if (unlink(pid_file) != 0) {
5449d3094edfSJørgen Austvik       vperror("Could not remove the pid file %s", pid_file);
5450032912ceSLisa Seelye   }
545186969ea4SBrad Fitzpatrick 
5452032912ceSLisa Seelye }
545386969ea4SBrad Fitzpatrick 
sig_handler(const int sig)545477dde9f9SPaul Lindner static void sig_handler(const int sig) {
5455f87ac298SMike Dillon     printf("Signal handled: %s.\n", strsignal(sig));
545677dde9f9SPaul Lindner     exit(EXIT_SUCCESS);
5457c9607c6dSBrad Fitzpatrick }
545886969ea4SBrad Fitzpatrick 
5459ee0c3d5aSTrond Norbye #ifndef HAVE_SIGIGNORE
sigignore(int sig)5460ee0c3d5aSTrond Norbye static int sigignore(int sig) {
5461ee0c3d5aSTrond Norbye     struct sigaction sa = { .sa_handler = SIG_IGN, .sa_flags = 0 };
5462ee0c3d5aSTrond Norbye 
5463ee0c3d5aSTrond Norbye     if (sigemptyset(&sa.sa_mask) == -1 || sigaction(sig, &sa, 0) == -1) {
5464ee0c3d5aSTrond Norbye         return -1;
5465ee0c3d5aSTrond Norbye     }
5466ee0c3d5aSTrond Norbye     return 0;
5467ee0c3d5aSTrond Norbye }
5468ee0c3d5aSTrond Norbye #endif
5469ee0c3d5aSTrond Norbye 
5470ee0c3d5aSTrond Norbye 
5471a6b35b44STrond Norbye /*
5472a6b35b44STrond Norbye  * On systems that supports multiple page sizes we may reduce the
5473a6b35b44STrond Norbye  * number of TLB-misses by using the biggest available page size
5474a6b35b44STrond Norbye  */
enable_large_pages(void)5475df1b7e42STrond Norbye static int enable_large_pages(void) {
547673628d3eSTrond Norbye #if defined(HAVE_GETPAGESIZES) && defined(HAVE_MEMCNTL)
5477a6b35b44STrond Norbye     int ret = -1;
5478a6b35b44STrond Norbye     size_t sizes[32];
5479a6b35b44STrond Norbye     int avail = getpagesizes(sizes, 32);
5480a6b35b44STrond Norbye     if (avail != -1) {
5481a6b35b44STrond Norbye         size_t max = sizes[0];
5482a6b35b44STrond Norbye         struct memcntl_mha arg = {0};
5483a6b35b44STrond Norbye         int ii;
5484a6b35b44STrond Norbye 
5485a6b35b44STrond Norbye         for (ii = 1; ii < avail; ++ii) {
5486a6b35b44STrond Norbye             if (max < sizes[ii]) {
5487a6b35b44STrond Norbye                 max = sizes[ii];
5488a6b35b44STrond Norbye             }
5489a6b35b44STrond Norbye         }
5490a6b35b44STrond Norbye 
5491a6b35b44STrond Norbye         arg.mha_flags   = 0;
5492a6b35b44STrond Norbye         arg.mha_pagesize = max;
5493a6b35b44STrond Norbye         arg.mha_cmd = MHA_MAPSIZE_BSSBRK;
5494a6b35b44STrond Norbye 
5495a6b35b44STrond Norbye         if (memcntl(0, 0, MC_HAT_ADVISE, (caddr_t)&arg, 0, 0) == -1) {
5496a6b35b44STrond Norbye             fprintf(stderr, "Failed to set large pages: %s\n",
5497a6b35b44STrond Norbye                     strerror(errno));
5498a6b35b44STrond Norbye             fprintf(stderr, "Will use default page size\n");
5499a6b35b44STrond Norbye         } else {
5500a6b35b44STrond Norbye             ret = 0;
5501a6b35b44STrond Norbye         }
5502a6b35b44STrond Norbye     } else {
5503a6b35b44STrond Norbye         fprintf(stderr, "Failed to get supported pagesizes: %s\n",
5504a6b35b44STrond Norbye                 strerror(errno));
5505a6b35b44STrond Norbye         fprintf(stderr, "Will use default page size\n");
5506a6b35b44STrond Norbye     }
5507a6b35b44STrond Norbye 
5508a6b35b44STrond Norbye     return ret;
550973628d3eSTrond Norbye #else
551055ef5d37Sdormando     return -1;
5511a6b35b44STrond Norbye #endif
551273628d3eSTrond Norbye }
5513a6b35b44STrond Norbye 
5514b9423044STrond Norbye /**
5515b9423044STrond Norbye  * Do basic sanity check of the runtime environment
5516b9423044STrond Norbye  * @return true if no errors found, false if we can't use this env
5517b9423044STrond Norbye  */
sanitycheck(void)5518b9423044STrond Norbye static bool sanitycheck(void) {
5519b9423044STrond Norbye     /* One of our biggest problems is old and bogus libevents */
5520b9423044STrond Norbye     const char *ever = event_get_version();
5521b9423044STrond Norbye     if (ever != NULL) {
5522b9423044STrond Norbye         if (strncmp(ever, "1.", 2) == 0) {
5523b9423044STrond Norbye             /* Require at least 1.3 (that's still a couple of years old) */
5524697cac04Smdl             if (('0' <= ever[2] && ever[2] < '3') && !isdigit(ever[3])) {
5525b9423044STrond Norbye                 fprintf(stderr, "You are using libevent %s.\nPlease upgrade to"
5526b9423044STrond Norbye                         " a more recent version (1.3 or newer)\n",
5527b9423044STrond Norbye                         event_get_version());
5528b9423044STrond Norbye                 return false;
5529b9423044STrond Norbye             }
5530b9423044STrond Norbye         }
5531b9423044STrond Norbye     }
5532b9423044STrond Norbye 
5533b9423044STrond Norbye     return true;
5534b9423044STrond Norbye }
5535b9423044STrond Norbye 
_parse_slab_sizes(char * s,uint32_t * slab_sizes)5536d7fb022dSdormando static bool _parse_slab_sizes(char *s, uint32_t *slab_sizes) {
5537d7fb022dSdormando     char *b = NULL;
5538d7fb022dSdormando     uint32_t size = 0;
5539d7fb022dSdormando     int i = 0;
5540d7fb022dSdormando     uint32_t last_size = 0;
5541d7fb022dSdormando 
5542d7fb022dSdormando     if (strlen(s) < 1)
5543d7fb022dSdormando         return false;
5544d7fb022dSdormando 
5545d7fb022dSdormando     for (char *p = strtok_r(s, "-", &b);
5546d7fb022dSdormando          p != NULL;
5547d7fb022dSdormando          p = strtok_r(NULL, "-", &b)) {
5548d7fb022dSdormando         if (!safe_strtoul(p, &size) || size < settings.chunk_size
554951a828b9Sdormando              || size > settings.slab_chunk_size_max) {
5550d7fb022dSdormando             fprintf(stderr, "slab size %u is out of valid range\n", size);
5551d7fb022dSdormando             return false;
5552d7fb022dSdormando         }
5553d7fb022dSdormando         if (last_size >= size) {
5554d7fb022dSdormando             fprintf(stderr, "slab size %u cannot be lower than or equal to a previous class size\n", size);
5555d7fb022dSdormando             return false;
5556d7fb022dSdormando         }
5557d7fb022dSdormando         if (size <= last_size + CHUNK_ALIGN_BYTES) {
5558d7fb022dSdormando             fprintf(stderr, "slab size %u must be at least %d bytes larger than previous class\n",
5559d7fb022dSdormando                     size, CHUNK_ALIGN_BYTES);
5560d7fb022dSdormando             return false;
5561d7fb022dSdormando         }
5562d7fb022dSdormando         slab_sizes[i++] = size;
5563d7fb022dSdormando         last_size = size;
5564d7fb022dSdormando         if (i >= MAX_NUMBER_OF_SLAB_CLASSES-1) {
5565d7fb022dSdormando             fprintf(stderr, "too many slab classes specified\n");
5566d7fb022dSdormando             return false;
5567d7fb022dSdormando         }
5568d7fb022dSdormando     }
5569d7fb022dSdormando 
5570d7fb022dSdormando     slab_sizes[i] = 0;
5571d7fb022dSdormando     return true;
5572d7fb022dSdormando }
5573d7fb022dSdormando 
main(int argc,char ** argv)557432f382b6SBrad Fitzpatrick int main (int argc, char **argv) {
557532f382b6SBrad Fitzpatrick     int c;
557677dde9f9SPaul Lindner     bool lock_memory = false;
557708c14e4eSTrond Norbye     bool do_daemonize = false;
5578a6b35b44STrond Norbye     bool preallocate = false;
55798da34dfcSAnatoly Vorobey     int maxcore = 0;
558077dde9f9SPaul Lindner     char *username = NULL;
5581c6975ef4SPaul Lindner     char *pid_file = NULL;
55824961114aSBrad Fitzpatrick     struct passwd *pw;
55835dc7dfa5SAnatoly Vorobey     struct rlimit rlim;
5584d5d4c490SAndrew Glinskiy     char *buf;
5585bed5f9bbSdormando     char unit = '\0';
5586bed5f9bbSdormando     int size_max = 0;
5587538919d8Sdormando     int retval = EXIT_SUCCESS;
55881d03d2d9SDustin Sallings     /* listening sockets */
55899150c85bSBrian Aker     static int *l_socket = NULL;
55909150c85bSBrian Aker 
55919150c85bSBrian Aker     /* udp socket */
55929150c85bSBrian Aker     static int *u_socket = NULL;
5593991ea276STrond Norbye     bool protocol_specified = false;
55946dc28e90SDustin Sallings     bool tcp_specified = false;
55956dc28e90SDustin Sallings     bool udp_specified = false;
5596c1f0262eSdormando     bool start_lru_maintainer = false;
5597c1f0262eSdormando     bool start_lru_crawler = false;
559805ca809cSdormando     enum hashfunc_type hash_type = JENKINS_HASH;
5599e31a5912Sdormando     uint32_t tocrawl;
5600d7fb022dSdormando     uint32_t slab_sizes[MAX_NUMBER_OF_SLAB_CLASSES];
5601d7fb022dSdormando     bool use_slab_sizes = false;
560251a828b9Sdormando     char *slab_sizes_unparsed = NULL;
560351a828b9Sdormando     bool slab_chunk_size_changed = false;
560486969ea4SBrad Fitzpatrick 
56054a4cac91SAntony Dovgal     char *subopts, *subopts_orig;
5606d1f9d992Sdormando     char *subopts_value;
5607d1f9d992Sdormando     enum {
56081db1de38Sdormando         MAXCONNS_FAST = 0,
560910698baeSdormando         HASHPOWER_INIT,
561010698baeSdormando         SLAB_REASSIGN,
5611058af0d8SKeyur         SLAB_AUTOMOVE,
561205ca809cSdormando         TAIL_REPAIR_TIME,
5613cb9cafa6Sdormando         HASH_ALGORITHM,
5614bb63a3edSdormando         LRU_CRAWLER,
5615e31a5912Sdormando         LRU_CRAWLER_SLEEP,
5616d9edfefdSdormando         LRU_CRAWLER_TOCRAWL,
56178d6bf78aSdormando         LRU_MAINTAINER,
56188d6bf78aSdormando         HOT_LRU_PCT,
56194de89c8cSdormando         WARM_LRU_PCT,
5620cad0ecbfSdormando         NOEXP_NOEVICT,
562183ba6bd9SJay Grizzard         IDLE_TIMEOUT,
5622d704f2c0Sdormando         WATCHER_LOGBUF_SIZE,
5623d704f2c0Sdormando         WORKER_LOGBUF_SIZE,
5624d7fb022dSdormando         SLAB_SIZES,
562551a828b9Sdormando         SLAB_CHUNK_MAX,
56268d82383fSdormando         TRACK_SIZES,
5627cad0ecbfSdormando         MODERN
5628d1f9d992Sdormando     };
5629d1f9d992Sdormando     char *const subopts_tokens[] = {
5630d1f9d992Sdormando         [MAXCONNS_FAST] = "maxconns_fast",
56311db1de38Sdormando         [HASHPOWER_INIT] = "hashpower",
563210698baeSdormando         [SLAB_REASSIGN] = "slab_reassign",
563310698baeSdormando         [SLAB_AUTOMOVE] = "slab_automove",
5634058af0d8SKeyur         [TAIL_REPAIR_TIME] = "tail_repair_time",
563505ca809cSdormando         [HASH_ALGORITHM] = "hash_algorithm",
5636bb63a3edSdormando         [LRU_CRAWLER] = "lru_crawler",
5637bb63a3edSdormando         [LRU_CRAWLER_SLEEP] = "lru_crawler_sleep",
5638e31a5912Sdormando         [LRU_CRAWLER_TOCRAWL] = "lru_crawler_tocrawl",
5639d9edfefdSdormando         [LRU_MAINTAINER] = "lru_maintainer",
56408d6bf78aSdormando         [HOT_LRU_PCT] = "hot_lru_pct",
56418d6bf78aSdormando         [WARM_LRU_PCT] = "warm_lru_pct",
56424de89c8cSdormando         [NOEXP_NOEVICT] = "expirezero_does_not_evict",
564383ba6bd9SJay Grizzard         [IDLE_TIMEOUT] = "idle_timeout",
5644d704f2c0Sdormando         [WATCHER_LOGBUF_SIZE] = "watcher_logbuf_size",
5645d704f2c0Sdormando         [WORKER_LOGBUF_SIZE] = "worker_logbuf_size",
5646d7fb022dSdormando         [SLAB_SIZES] = "slab_sizes",
564751a828b9Sdormando         [SLAB_CHUNK_MAX] = "slab_chunk_max",
56488d82383fSdormando         [TRACK_SIZES] = "track_sizes",
5649cad0ecbfSdormando         [MODERN] = "modern",
5650d1f9d992Sdormando         NULL
5651d1f9d992Sdormando     };
5652d1f9d992Sdormando 
5653b9423044STrond Norbye     if (!sanitycheck()) {
5654b9423044STrond Norbye         return EX_OSERR;
5655b9423044STrond Norbye     }
5656b9423044STrond Norbye 
5657f87ac298SMike Dillon     /* handle SIGINT and SIGTERM */
5658c9607c6dSBrad Fitzpatrick     signal(SIGINT, sig_handler);
5659f87ac298SMike Dillon     signal(SIGTERM, sig_handler);
566086969ea4SBrad Fitzpatrick 
566160d70942SAnatoly Vorobey     /* init settings */
566232f382b6SBrad Fitzpatrick     settings_init();
566386969ea4SBrad Fitzpatrick 
56644ceefe3eSdormando     /* Run regardless of initializing it later */
56654ceefe3eSdormando     init_lru_crawler();
5666fb269897Sdormando     init_lru_maintainer();
56674ceefe3eSdormando 
5668146ba544SPeter van Dijk     /* set stderr non-buffering (for running under, say, daemontools) */
5669146ba544SPeter van Dijk     setbuf(stderr, NULL);
567086969ea4SBrad Fitzpatrick 
567132f382b6SBrad Fitzpatrick     /* process arguments */
567288bcc852SClinton Webb     while (-1 != (c = getopt(argc, argv,
567388bcc852SClinton Webb           "a:"  /* access mask for unix socket */
5674d11dc0eaSBrian Aker           "A"  /* enable admin shutdown commannd */
567588bcc852SClinton Webb           "p:"  /* TCP port number to listen on */
567688bcc852SClinton Webb           "s:"  /* unix socket path to listen on */
567788bcc852SClinton Webb           "U:"  /* UDP port number to listen on */
567888bcc852SClinton Webb           "m:"  /* max memory to use for items in megabytes */
567988bcc852SClinton Webb           "M"   /* return error on memory exhausted */
568088bcc852SClinton Webb           "c:"  /* max simultaneous connections */
568188bcc852SClinton Webb           "k"   /* lock down all paged memory */
5682dda51c02SDan McGee           "hiV" /* help, licence info, version */
568388bcc852SClinton Webb           "r"   /* maximize core file limit */
568488bcc852SClinton Webb           "v"   /* verbose */
568588bcc852SClinton Webb           "d"   /* daemon mode */
568688bcc852SClinton Webb           "l:"  /* interface to listen on */
568788bcc852SClinton Webb           "u:"  /* user identity to run as */
568888bcc852SClinton Webb           "P:"  /* save PID in file */
568988bcc852SClinton Webb           "f:"  /* factor? */
569088bcc852SClinton Webb           "n:"  /* minimum space allocated for key+value+flags */
569188bcc852SClinton Webb           "t:"  /* threads */
569288bcc852SClinton Webb           "D:"  /* prefix delimiter? */
569388bcc852SClinton Webb           "L"   /* Large memory pages */
569488bcc852SClinton Webb           "R:"  /* max requests per event */
56951b4a42a2SDustin Sallings           "C"   /* Disable use of CAS */
56961b4a42a2SDustin Sallings           "b:"  /* backlog queue limit */
5697a155b044SDustin Sallings           "B:"  /* Binding protocol */
5698bed5f9bbSdormando           "I:"  /* Max item size */
5699f1307c4dSDustin Sallings           "S"   /* Sasl ON */
5700a2f5ca50SDaniel Pañeda           "F"   /* Disable flush_all */
5701d1f9d992Sdormando           "o:"  /* Extended generic options */
570288bcc852SClinton Webb         ))) {
570332f382b6SBrad Fitzpatrick         switch (c) {
5704d11dc0eaSBrian Aker         case 'A':
5705d11dc0eaSBrian Aker             /* enables "shutdown" command */
5706d11dc0eaSBrian Aker             settings.shutdown_command = true;
5707d11dc0eaSBrian Aker             break;
5708d11dc0eaSBrian Aker 
570940c76cedSDavid Bremner         case 'a':
571040c76cedSDavid Bremner             /* access for unix domain socket, as octal mask (like chmod)*/
571140c76cedSDavid Bremner             settings.access= strtol(optarg,NULL,8);
571240c76cedSDavid Bremner             break;
571340c76cedSDavid Bremner 
5714c9607c6dSBrad Fitzpatrick         case 'U':
5715c9607c6dSBrad Fitzpatrick             settings.udpport = atoi(optarg);
57166dc28e90SDustin Sallings             udp_specified = true;
5717c9607c6dSBrad Fitzpatrick             break;
571832f382b6SBrad Fitzpatrick         case 'p':
571932f382b6SBrad Fitzpatrick             settings.port = atoi(optarg);
57206dc28e90SDustin Sallings             tcp_specified = true;
572132f382b6SBrad Fitzpatrick             break;
5722c9607c6dSBrad Fitzpatrick         case 's':
5723c9607c6dSBrad Fitzpatrick             settings.socketpath = optarg;
5724c9607c6dSBrad Fitzpatrick             break;
572532f382b6SBrad Fitzpatrick         case 'm':
5726c9607c6dSBrad Fitzpatrick             settings.maxbytes = ((size_t)atoi(optarg)) * 1024 * 1024;
572732f382b6SBrad Fitzpatrick             break;
5728841811e9SJason Titus         case 'M':
5729841811e9SJason Titus             settings.evict_to_free = 0;
5730841811e9SJason Titus             break;
573132f382b6SBrad Fitzpatrick         case 'c':
573232f382b6SBrad Fitzpatrick             settings.maxconns = atoi(optarg);
57330fe9eac9Ssergiocarlos             if (settings.maxconns <= 0) {
57340fe9eac9Ssergiocarlos                 fprintf(stderr, "Maximum connections must be greater than 0\n");
57350fe9eac9Ssergiocarlos                 return 1;
57360fe9eac9Ssergiocarlos             }
573732f382b6SBrad Fitzpatrick             break;
573832f382b6SBrad Fitzpatrick         case 'h':
573932f382b6SBrad Fitzpatrick             usage();
574077dde9f9SPaul Lindner             exit(EXIT_SUCCESS);
57410c3b47f4SBrad Fitzpatrick         case 'i':
57420c3b47f4SBrad Fitzpatrick             usage_license();
574377dde9f9SPaul Lindner             exit(EXIT_SUCCESS);
5744dda51c02SDan McGee         case 'V':
5745dda51c02SDan McGee             printf(PACKAGE " " VERSION "\n");
5746dda51c02SDan McGee             exit(EXIT_SUCCESS);
574732f382b6SBrad Fitzpatrick         case 'k':
574877dde9f9SPaul Lindner             lock_memory = true;
574932f382b6SBrad Fitzpatrick             break;
575060d70942SAnatoly Vorobey         case 'v':
57519aa4cab2SEvan Martin             settings.verbose++;
575260d70942SAnatoly Vorobey             break;
575332f382b6SBrad Fitzpatrick         case 'l':
57546ba9aa27STrond Norbye             if (settings.inter != NULL) {
57558c65b244SSharif Nassar                 if (strstr(settings.inter, optarg) != NULL) {
57568c65b244SSharif Nassar                     break;
57578c65b244SSharif Nassar                 }
57586ba9aa27STrond Norbye                 size_t len = strlen(settings.inter) + strlen(optarg) + 2;
57596ba9aa27STrond Norbye                 char *p = malloc(len);
57606ba9aa27STrond Norbye                 if (p == NULL) {
57616ba9aa27STrond Norbye                     fprintf(stderr, "Failed to allocate memory\n");
57626ba9aa27STrond Norbye                     return 1;
57636ba9aa27STrond Norbye                 }
57646ba9aa27STrond Norbye                 snprintf(p, len, "%s,%s", settings.inter, optarg);
57656ba9aa27STrond Norbye                 free(settings.inter);
57666ba9aa27STrond Norbye                 settings.inter = p;
57676ba9aa27STrond Norbye             } else {
5768a61a6900SBrian Aker                 settings.inter= strdup(optarg);
57696ba9aa27STrond Norbye             }
577032f382b6SBrad Fitzpatrick             break;
577132f382b6SBrad Fitzpatrick         case 'd':
577208c14e4eSTrond Norbye             do_daemonize = true;
577332f382b6SBrad Fitzpatrick             break;
57748da34dfcSAnatoly Vorobey         case 'r':
57758da34dfcSAnatoly Vorobey             maxcore = 1;
57768da34dfcSAnatoly Vorobey             break;
5777ca90710fSdormando         case 'R':
5778ca90710fSdormando             settings.reqs_per_event = atoi(optarg);
5779ca90710fSdormando             if (settings.reqs_per_event == 0) {
5780ca90710fSdormando                 fprintf(stderr, "Number of requests per event must be greater than 0\n");
5781ca90710fSdormando                 return 1;
5782ca90710fSdormando             }
5783ca90710fSdormando             break;
57844961114aSBrad Fitzpatrick         case 'u':
57854961114aSBrad Fitzpatrick             username = optarg;
57864961114aSBrad Fitzpatrick             break;
5787032912ceSLisa Seelye         case 'P':
5788032912ceSLisa Seelye             pid_file = optarg;
5789032912ceSLisa Seelye             break;
5790c9607c6dSBrad Fitzpatrick         case 'f':
5791c9607c6dSBrad Fitzpatrick             settings.factor = atof(optarg);
5792c9607c6dSBrad Fitzpatrick             if (settings.factor <= 1.0) {
5793c9607c6dSBrad Fitzpatrick                 fprintf(stderr, "Factor must be greater than 1\n");
5794c9607c6dSBrad Fitzpatrick                 return 1;
5795c9607c6dSBrad Fitzpatrick             }
5796c9607c6dSBrad Fitzpatrick             break;
5797c9607c6dSBrad Fitzpatrick         case 'n':
5798c9607c6dSBrad Fitzpatrick             settings.chunk_size = atoi(optarg);
5799c9607c6dSBrad Fitzpatrick             if (settings.chunk_size == 0) {
5800c9607c6dSBrad Fitzpatrick                 fprintf(stderr, "Chunk size must be greater than 0\n");
5801c9607c6dSBrad Fitzpatrick                 return 1;
5802c9607c6dSBrad Fitzpatrick             }
5803c9607c6dSBrad Fitzpatrick             break;
580456b8339eSSteven Grimm         case 't':
58052fe44f1cSDmitry Isaykin             settings.num_threads = atoi(optarg);
5806dd181009Sdormando             if (settings.num_threads <= 0) {
580756b8339eSSteven Grimm                 fprintf(stderr, "Number of threads must be greater than 0\n");
580856b8339eSSteven Grimm                 return 1;
580956b8339eSSteven Grimm             }
5810dd181009Sdormando             /* There're other problems when you get above 64 threads.
5811dd181009Sdormando              * In the future we should portably detect # of cores for the
5812dd181009Sdormando              * default.
5813dd181009Sdormando              */
5814dd181009Sdormando             if (settings.num_threads > 64) {
5815dd181009Sdormando                 fprintf(stderr, "WARNING: Setting a high number of worker"
5816dd181009Sdormando                                 "threads is not recommended.\n"
5817dd181009Sdormando                                 " Set this value to the number of cores in"
5818dd181009Sdormando                                 " your machine or less.\n");
5819dd181009Sdormando             }
582056b8339eSSteven Grimm             break;
582156b8339eSSteven Grimm         case 'D':
582256b8339eSSteven Grimm             if (! optarg || ! optarg[0]) {
582356b8339eSSteven Grimm                 fprintf(stderr, "No delimiter specified\n");
582456b8339eSSteven Grimm                 return 1;
582556b8339eSSteven Grimm             }
582656b8339eSSteven Grimm             settings.prefix_delimiter = optarg[0];
582756b8339eSSteven Grimm             settings.detail_enabled = 1;
582856b8339eSSteven Grimm             break;
5829a6b35b44STrond Norbye         case 'L' :
5830a6b35b44STrond Norbye             if (enable_large_pages() == 0) {
5831a6b35b44STrond Norbye                 preallocate = true;
583255ef5d37Sdormando             } else {
5833c979d213Sdormando                 fprintf(stderr, "Cannot enable large pages on this system\n"
5834c979d213Sdormando                     "(There is no Linux support as of this version)\n");
583555ef5d37Sdormando                 return 1;
5836a6b35b44STrond Norbye             }
5837f1351f9bSTrond Norbye             break;
5838eda68b70STrond Norbye         case 'C' :
5839eda68b70STrond Norbye             settings.use_cas = false;
5840eda68b70STrond Norbye             break;
58417d010a85SChris Goffinet         case 'b' :
58427d010a85SChris Goffinet             settings.backlog = atoi(optarg);
58437d010a85SChris Goffinet             break;
5844a155b044SDustin Sallings         case 'B':
5845991ea276STrond Norbye             protocol_specified = true;
5846a155b044SDustin Sallings             if (strcmp(optarg, "auto") == 0) {
5847a155b044SDustin Sallings                 settings.binding_protocol = negotiating_prot;
5848a155b044SDustin Sallings             } else if (strcmp(optarg, "binary") == 0) {
5849a155b044SDustin Sallings                 settings.binding_protocol = binary_prot;
5850a155b044SDustin Sallings             } else if (strcmp(optarg, "ascii") == 0) {
5851a155b044SDustin Sallings                 settings.binding_protocol = ascii_prot;
5852a155b044SDustin Sallings             } else {
5853a155b044SDustin Sallings                 fprintf(stderr, "Invalid value for binding protocol: %s\n"
5854a155b044SDustin Sallings                         " -- should be one of auto, binary, or ascii\n", optarg);
5855a155b044SDustin Sallings                 exit(EX_USAGE);
5856a155b044SDustin Sallings             }
5857a155b044SDustin Sallings             break;
5858bed5f9bbSdormando         case 'I':
5859d5d4c490SAndrew Glinskiy             buf = strdup(optarg);
5860d5d4c490SAndrew Glinskiy             unit = buf[strlen(buf)-1];
5861bed5f9bbSdormando             if (unit == 'k' || unit == 'm' ||
5862bed5f9bbSdormando                 unit == 'K' || unit == 'M') {
5863d5d4c490SAndrew Glinskiy                 buf[strlen(buf)-1] = '\0';
5864d5d4c490SAndrew Glinskiy                 size_max = atoi(buf);
5865bed5f9bbSdormando                 if (unit == 'k' || unit == 'K')
5866bed5f9bbSdormando                     size_max *= 1024;
5867bed5f9bbSdormando                 if (unit == 'm' || unit == 'M')
5868bed5f9bbSdormando                     size_max *= 1024 * 1024;
5869bed5f9bbSdormando                 settings.item_size_max = size_max;
5870bed5f9bbSdormando             } else {
5871d5d4c490SAndrew Glinskiy                 settings.item_size_max = atoi(buf);
5872bed5f9bbSdormando             }
587345383f05SYongyue Sun             free(buf);
5874bed5f9bbSdormando             if (settings.item_size_max < 1024) {
5875bed5f9bbSdormando                 fprintf(stderr, "Item max size cannot be less than 1024 bytes.\n");
5876bed5f9bbSdormando                 return 1;
5877bed5f9bbSdormando             }
587851a828b9Sdormando             if (settings.item_size_max > (settings.maxbytes / 4)) {
587951a828b9Sdormando                 fprintf(stderr, "Cannot set item size limit higher than 1/4 of memory max.\n");
5880bed5f9bbSdormando                 return 1;
5881bed5f9bbSdormando             }
5882bed5f9bbSdormando             if (settings.item_size_max > 1024 * 1024) {
588351a828b9Sdormando                 if (!slab_chunk_size_changed) {
588451a828b9Sdormando                     settings.slab_chunk_size_max = 16384;
588551a828b9Sdormando                 }
5886bed5f9bbSdormando             }
5887bed5f9bbSdormando             break;
5888f1307c4dSDustin Sallings         case 'S': /* set Sasl authentication to true. Default is false */
5889f1307c4dSDustin Sallings #ifndef ENABLE_SASL
5890f1307c4dSDustin Sallings             fprintf(stderr, "This server is not built with SASL support.\n");
5891f1307c4dSDustin Sallings             exit(EX_USAGE);
5892f1307c4dSDustin Sallings #endif
5893f1307c4dSDustin Sallings             settings.sasl = true;
5894f1307c4dSDustin Sallings             break;
5895a2f5ca50SDaniel Pañeda        case 'F' :
5896a2f5ca50SDaniel Pañeda             settings.flush_enabled = false;
5897a2f5ca50SDaniel Pañeda             break;
5898d1f9d992Sdormando         case 'o': /* It's sub-opts time! */
58994a4cac91SAntony Dovgal             subopts_orig = subopts = strdup(optarg); /* getsubopt() changes the original args */
5900d1f9d992Sdormando 
5901d1f9d992Sdormando             while (*subopts != '\0') {
5902d1f9d992Sdormando 
5903d1f9d992Sdormando             switch (getsubopt(&subopts, subopts_tokens, &subopts_value)) {
5904d1f9d992Sdormando             case MAXCONNS_FAST:
5905d1f9d992Sdormando                 settings.maxconns_fast = true;
5906d1f9d992Sdormando                 break;
59071db1de38Sdormando             case HASHPOWER_INIT:
59081db1de38Sdormando                 if (subopts_value == NULL) {
59091db1de38Sdormando                     fprintf(stderr, "Missing numeric argument for hashpower\n");
59101db1de38Sdormando                     return 1;
59111db1de38Sdormando                 }
59121db1de38Sdormando                 settings.hashpower_init = atoi(subopts_value);
59131db1de38Sdormando                 if (settings.hashpower_init < 12) {
59141db1de38Sdormando                     fprintf(stderr, "Initial hashtable multiplier of %d is too low\n",
59151db1de38Sdormando                         settings.hashpower_init);
59161db1de38Sdormando                     return 1;
59171db1de38Sdormando                 } else if (settings.hashpower_init > 64) {
59181db1de38Sdormando                     fprintf(stderr, "Initial hashtable multiplier of %d is too high\n"
59191db1de38Sdormando                         "Choose a value based on \"STAT hash_power_level\" from a running instance\n",
59201db1de38Sdormando                         settings.hashpower_init);
59211db1de38Sdormando                     return 1;
59221db1de38Sdormando                 }
59231db1de38Sdormando                 break;
592410698baeSdormando             case SLAB_REASSIGN:
592510698baeSdormando                 settings.slab_reassign = true;
592610698baeSdormando                 break;
592710698baeSdormando             case SLAB_AUTOMOVE:
592863bf748aSdormando                 if (subopts_value == NULL) {
592963bf748aSdormando                     settings.slab_automove = 1;
593063bf748aSdormando                     break;
593163bf748aSdormando                 }
593263bf748aSdormando                 settings.slab_automove = atoi(subopts_value);
593363bf748aSdormando                 if (settings.slab_automove < 0 || settings.slab_automove > 2) {
593463bf748aSdormando                     fprintf(stderr, "slab_automove must be between 0 and 2\n");
593563bf748aSdormando                     return 1;
593663bf748aSdormando                 }
593710698baeSdormando                 break;
5938058af0d8SKeyur             case TAIL_REPAIR_TIME:
5939058af0d8SKeyur                 if (subopts_value == NULL) {
5940058af0d8SKeyur                     fprintf(stderr, "Missing numeric argument for tail_repair_time\n");
5941058af0d8SKeyur                     return 1;
5942058af0d8SKeyur                 }
5943058af0d8SKeyur                 settings.tail_repair_time = atoi(subopts_value);
59445ce6a38bSdormando                 if (settings.tail_repair_time < 10) {
59455ce6a38bSdormando                     fprintf(stderr, "Cannot set tail_repair_time to less than 10 seconds\n");
59465ce6a38bSdormando                     return 1;
59475ce6a38bSdormando                 }
5948058af0d8SKeyur                 break;
594905ca809cSdormando             case HASH_ALGORITHM:
595005ca809cSdormando                 if (subopts_value == NULL) {
595105ca809cSdormando                     fprintf(stderr, "Missing hash_algorithm argument\n");
595205ca809cSdormando                     return 1;
595305ca809cSdormando                 };
595405ca809cSdormando                 if (strcmp(subopts_value, "jenkins") == 0) {
595505ca809cSdormando                     hash_type = JENKINS_HASH;
595605ca809cSdormando                 } else if (strcmp(subopts_value, "murmur3") == 0) {
595705ca809cSdormando                     hash_type = MURMUR3_HASH;
595805ca809cSdormando                 } else {
595905ca809cSdormando                     fprintf(stderr, "Unknown hash_algorithm option (jenkins, murmur3)\n");
596005ca809cSdormando                     return 1;
596105ca809cSdormando                 }
596205ca809cSdormando                 break;
5963bb63a3edSdormando             case LRU_CRAWLER:
5964c1f0262eSdormando                 start_lru_crawler = true;
5965bb63a3edSdormando                 break;
5966bb63a3edSdormando             case LRU_CRAWLER_SLEEP:
5967a7e54510SAntony Dovgal                 if (subopts_value == NULL) {
5968a7e54510SAntony Dovgal                     fprintf(stderr, "Missing lru_crawler_sleep value\n");
5969a7e54510SAntony Dovgal                     return 1;
5970a7e54510SAntony Dovgal                 }
5971bb63a3edSdormando                 settings.lru_crawler_sleep = atoi(subopts_value);
5972bb63a3edSdormando                 if (settings.lru_crawler_sleep > 1000000 || settings.lru_crawler_sleep < 0) {
5973bb63a3edSdormando                     fprintf(stderr, "LRU crawler sleep must be between 0 and 1 second\n");
5974bb63a3edSdormando                     return 1;
5975bb63a3edSdormando                 }
5976bb63a3edSdormando                 break;
5977e31a5912Sdormando             case LRU_CRAWLER_TOCRAWL:
5978a7e54510SAntony Dovgal                 if (subopts_value == NULL) {
5979a7e54510SAntony Dovgal                     fprintf(stderr, "Missing lru_crawler_tocrawl value\n");
5980a7e54510SAntony Dovgal                     return 1;
5981a7e54510SAntony Dovgal                 }
5982e31a5912Sdormando                 if (!safe_strtoul(subopts_value, &tocrawl)) {
5983e31a5912Sdormando                     fprintf(stderr, "lru_crawler_tocrawl takes a numeric 32bit value\n");
5984e31a5912Sdormando                     return 1;
5985e31a5912Sdormando                 }
5986e31a5912Sdormando                 settings.lru_crawler_tocrawl = tocrawl;
5987e31a5912Sdormando                 break;
5988d9edfefdSdormando             case LRU_MAINTAINER:
5989c1f0262eSdormando                 start_lru_maintainer = true;
5990d9edfefdSdormando                 break;
59918d6bf78aSdormando             case HOT_LRU_PCT:
59928d6bf78aSdormando                 if (subopts_value == NULL) {
59938d6bf78aSdormando                     fprintf(stderr, "Missing hot_lru_pct argument\n");
59948d6bf78aSdormando                     return 1;
59958d6bf78aSdormando                 };
59968d6bf78aSdormando                 settings.hot_lru_pct = atoi(subopts_value);
59978d6bf78aSdormando                 if (settings.hot_lru_pct < 1 || settings.hot_lru_pct >= 80) {
59988d6bf78aSdormando                     fprintf(stderr, "hot_lru_pct must be > 1 and < 80\n");
59998d6bf78aSdormando                     return 1;
60008d6bf78aSdormando                 }
60018d6bf78aSdormando                 break;
60028d6bf78aSdormando             case WARM_LRU_PCT:
60038d6bf78aSdormando                 if (subopts_value == NULL) {
60048d6bf78aSdormando                     fprintf(stderr, "Missing warm_lru_pct argument\n");
60058d6bf78aSdormando                     return 1;
60068d6bf78aSdormando                 };
60078d6bf78aSdormando                 settings.warm_lru_pct = atoi(subopts_value);
60088d6bf78aSdormando                 if (settings.warm_lru_pct < 1 || settings.warm_lru_pct >= 80) {
60098d6bf78aSdormando                     fprintf(stderr, "warm_lru_pct must be > 1 and < 80\n");
60108d6bf78aSdormando                     return 1;
60118d6bf78aSdormando                 }
60128d6bf78aSdormando                 break;
60134de89c8cSdormando             case NOEXP_NOEVICT:
60144de89c8cSdormando                 settings.expirezero_does_not_evict = true;
60154de89c8cSdormando                 break;
601683ba6bd9SJay Grizzard             case IDLE_TIMEOUT:
601783ba6bd9SJay Grizzard                 settings.idle_timeout = atoi(subopts_value);
601883ba6bd9SJay Grizzard                 break;
6019d704f2c0Sdormando             case WATCHER_LOGBUF_SIZE:
6020d704f2c0Sdormando                 if (subopts_value == NULL) {
6021d704f2c0Sdormando                     fprintf(stderr, "Missing watcher_logbuf_size argument\n");
6022d704f2c0Sdormando                     return 1;
6023d704f2c0Sdormando                 }
6024d704f2c0Sdormando                 if (!safe_strtoul(subopts_value, &settings.logger_watcher_buf_size)) {
6025d704f2c0Sdormando                     fprintf(stderr, "could not parse argument to watcher_logbuf_size\n");
6026d704f2c0Sdormando                     return 1;
6027d704f2c0Sdormando                 }
6028d704f2c0Sdormando                 settings.logger_watcher_buf_size *= 1024; /* kilobytes */
6029d704f2c0Sdormando                 break;
6030d704f2c0Sdormando             case WORKER_LOGBUF_SIZE:
6031d704f2c0Sdormando                 if (subopts_value == NULL) {
6032d704f2c0Sdormando                     fprintf(stderr, "Missing worker_logbuf_size argument\n");
6033d704f2c0Sdormando                     return 1;
6034d704f2c0Sdormando                 }
6035d704f2c0Sdormando                 if (!safe_strtoul(subopts_value, &settings.logger_buf_size)) {
6036d704f2c0Sdormando                     fprintf(stderr, "could not parse argument to worker_logbuf_size\n");
6037d704f2c0Sdormando                     return 1;
6038d704f2c0Sdormando                 }
6039d704f2c0Sdormando                 settings.logger_buf_size *= 1024; /* kilobytes */
6040d7fb022dSdormando             case SLAB_SIZES:
604151a828b9Sdormando                 slab_sizes_unparsed = subopts_value;
604251a828b9Sdormando                 break;
604351a828b9Sdormando             case SLAB_CHUNK_MAX:
604451a828b9Sdormando                 if (subopts_value == NULL) {
604551a828b9Sdormando                     fprintf(stderr, "Missing slab_chunk_max argument\n");
6046d7fb022dSdormando                 }
604751a828b9Sdormando                 if (!safe_strtol(subopts_value, &settings.slab_chunk_size_max)) {
604851a828b9Sdormando                     fprintf(stderr, "could not parse argument to slab_chunk_max\n");
604951a828b9Sdormando                 }
605051a828b9Sdormando                 slab_chunk_size_changed = true;
6051d704f2c0Sdormando                 break;
60528d82383fSdormando             case TRACK_SIZES:
60538d82383fSdormando                 item_stats_sizes_init();
60548d82383fSdormando                 break;
6055cad0ecbfSdormando             case MODERN:
6056cad0ecbfSdormando                 /* Modernized defaults. Need to add equivalent no_* flags
6057cad0ecbfSdormando                  * before making truly default. */
605851a828b9Sdormando                 settings.slab_chunk_size_max = 16384;
605951a828b9Sdormando                 settings.factor = 1.08;
6060cad0ecbfSdormando                 settings.slab_reassign = true;
6061cad0ecbfSdormando                 settings.slab_automove = 1;
6062cad0ecbfSdormando                 settings.maxconns_fast = true;
6063cad0ecbfSdormando                 hash_type = MURMUR3_HASH;
6064cad0ecbfSdormando                 start_lru_crawler = true;
6065cad0ecbfSdormando                 start_lru_maintainer = true;
6066cad0ecbfSdormando                 break;
6067d1f9d992Sdormando             default:
6068d1f9d992Sdormando                 printf("Illegal suboption \"%s\"\n", subopts_value);
6069d1f9d992Sdormando                 return 1;
6070d1f9d992Sdormando             }
6071d1f9d992Sdormando 
6072d1f9d992Sdormando             }
60734a4cac91SAntony Dovgal             free(subopts_orig);
6074d1f9d992Sdormando             break;
607532f382b6SBrad Fitzpatrick         default:
607632f382b6SBrad Fitzpatrick             fprintf(stderr, "Illegal argument \"%c\"\n", c);
607732f382b6SBrad Fitzpatrick             return 1;
607832f382b6SBrad Fitzpatrick         }
607932f382b6SBrad Fitzpatrick     }
608086969ea4SBrad Fitzpatrick 
608151a828b9Sdormando     if (settings.slab_chunk_size_max > settings.item_size_max) {
608251a828b9Sdormando         fprintf(stderr, "slab_chunk_max (bytes: %d) cannot be larger than -I (item_size_max %d)\n",
608351a828b9Sdormando                 settings.slab_chunk_size_max, settings.item_size_max);
608451a828b9Sdormando         exit(EX_USAGE);
608551a828b9Sdormando     }
608651a828b9Sdormando 
608751a828b9Sdormando     if (settings.item_size_max % settings.slab_chunk_size_max != 0) {
608851a828b9Sdormando         fprintf(stderr, "-I (item_size_max: %d) must be evenly divisible by slab_chunk_max (bytes: %d)\n",
608951a828b9Sdormando                 settings.item_size_max, settings.slab_chunk_size_max);
609051a828b9Sdormando         exit(EX_USAGE);
609151a828b9Sdormando     }
609251a828b9Sdormando 
609351a828b9Sdormando     if (settings.slab_page_size % settings.slab_chunk_size_max != 0) {
609451a828b9Sdormando         fprintf(stderr, "slab_chunk_max (bytes: %d) must divide evenly into %d (slab_page_size)\n",
609551a828b9Sdormando                 settings.slab_chunk_size_max, settings.slab_page_size);
609651a828b9Sdormando         exit(EX_USAGE);
609751a828b9Sdormando     }
609851a828b9Sdormando 
609951a828b9Sdormando     if (slab_sizes_unparsed != NULL) {
610051a828b9Sdormando         if (_parse_slab_sizes(subopts_value, slab_sizes)) {
610151a828b9Sdormando             use_slab_sizes = true;
610251a828b9Sdormando         } else {
610351a828b9Sdormando             exit(EX_USAGE);
610451a828b9Sdormando         }
610551a828b9Sdormando     }
610651a828b9Sdormando 
61078d6bf78aSdormando     if (settings.lru_maintainer_thread && settings.hot_lru_pct + settings.warm_lru_pct > 80) {
61088d6bf78aSdormando         fprintf(stderr, "hot_lru_pct + warm_lru_pct cannot be more than 80%% combined\n");
61098d6bf78aSdormando         exit(EX_USAGE);
61108d6bf78aSdormando     }
61118d6bf78aSdormando 
611205ca809cSdormando     if (hash_init(hash_type) != 0) {
611305ca809cSdormando         fprintf(stderr, "Failed to initialize hash_algorithm!\n");
611405ca809cSdormando         exit(EX_USAGE);
611505ca809cSdormando     }
611605ca809cSdormando 
6117c60ca35bSTrond Norbye     /*
6118c60ca35bSTrond Norbye      * Use one workerthread to serve each UDP port if the user specified
6119c60ca35bSTrond Norbye      * multiple ports
6120c60ca35bSTrond Norbye      */
6121c60ca35bSTrond Norbye     if (settings.inter != NULL && strchr(settings.inter, ',')) {
6122c60ca35bSTrond Norbye         settings.num_threads_per_udp = 1;
6123c60ca35bSTrond Norbye     } else {
6124c60ca35bSTrond Norbye         settings.num_threads_per_udp = settings.num_threads;
6125c60ca35bSTrond Norbye     }
6126c60ca35bSTrond Norbye 
6127991ea276STrond Norbye     if (settings.sasl) {
6128991ea276STrond Norbye         if (!protocol_specified) {
6129991ea276STrond Norbye             settings.binding_protocol = binary_prot;
6130991ea276STrond Norbye         } else {
6131991ea276STrond Norbye             if (settings.binding_protocol != binary_prot) {
61325100e7afSMatt Ingenthron                 fprintf(stderr, "ERROR: You cannot allow the ASCII protocol while using SASL.\n");
6133991ea276STrond Norbye                 exit(EX_USAGE);
6134991ea276STrond Norbye             }
6135991ea276STrond Norbye         }
6136991ea276STrond Norbye     }
6137991ea276STrond Norbye 
61386dc28e90SDustin Sallings     if (tcp_specified && !udp_specified) {
61396dc28e90SDustin Sallings         settings.udpport = settings.port;
61406dc28e90SDustin Sallings     } else if (udp_specified && !tcp_specified) {
61416dc28e90SDustin Sallings         settings.port = settings.udpport;
61426dc28e90SDustin Sallings     }
61436dc28e90SDustin Sallings 
614477dde9f9SPaul Lindner     if (maxcore != 0) {
61458da34dfcSAnatoly Vorobey         struct rlimit rlim_new;
61468da34dfcSAnatoly Vorobey         /*
61478da34dfcSAnatoly Vorobey          * First try raising to infinity; if that fails, try bringing
61488da34dfcSAnatoly Vorobey          * the soft limit to the hard.
61498da34dfcSAnatoly Vorobey          */
61508da34dfcSAnatoly Vorobey         if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
61518da34dfcSAnatoly Vorobey             rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
61528da34dfcSAnatoly Vorobey             if (setrlimit(RLIMIT_CORE, &rlim_new)!= 0) {
61538da34dfcSAnatoly Vorobey                 /* failed. try raising just to the old max */
615478955139STim Yardley                 rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
61558da34dfcSAnatoly Vorobey                 (void)setrlimit(RLIMIT_CORE, &rlim_new);
61568da34dfcSAnatoly Vorobey             }
61578da34dfcSAnatoly Vorobey         }
61588da34dfcSAnatoly Vorobey         /*
61598da34dfcSAnatoly Vorobey          * getrlimit again to see what we ended up with. Only fail if
61608da34dfcSAnatoly Vorobey          * the soft limit ends up 0, because then no core files will be
61618da34dfcSAnatoly Vorobey          * created at all.
61628da34dfcSAnatoly Vorobey          */
616386969ea4SBrad Fitzpatrick 
61648da34dfcSAnatoly Vorobey         if ((getrlimit(RLIMIT_CORE, &rlim) != 0) || rlim.rlim_cur == 0) {
61658da34dfcSAnatoly Vorobey             fprintf(stderr, "failed to ensure corefile creation\n");
61667d8344ebSDustin Sallings             exit(EX_OSERR);
61678da34dfcSAnatoly Vorobey         }
61688da34dfcSAnatoly Vorobey     }
616986969ea4SBrad Fitzpatrick 
6170ed0f5b0eSBrion Vibber     /*
61715dc7dfa5SAnatoly Vorobey      * If needed, increase rlimits to allow as many connections
61725dc7dfa5SAnatoly Vorobey      * as needed.
61735dc7dfa5SAnatoly Vorobey      */
617486969ea4SBrad Fitzpatrick 
61755dc7dfa5SAnatoly Vorobey     if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
61765dc7dfa5SAnatoly Vorobey         fprintf(stderr, "failed to getrlimit number of files\n");
61777d8344ebSDustin Sallings         exit(EX_OSERR);
61785dc7dfa5SAnatoly Vorobey     } else {
6179d1f9d992Sdormando         rlim.rlim_cur = settings.maxconns;
6180cd6fc9a8Sdormando         rlim.rlim_max = settings.maxconns;
61815dc7dfa5SAnatoly Vorobey         if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
6182d1f9d992Sdormando             fprintf(stderr, "failed to set rlimit for open files. Try starting as root or requesting smaller maxconns value.\n");
61837d8344ebSDustin Sallings             exit(EX_OSERR);
61845dc7dfa5SAnatoly Vorobey         }
61855dc7dfa5SAnatoly Vorobey     }
618686969ea4SBrad Fitzpatrick 
61875ba315a1SDustin Sallings     /* lose root privileges if we have them */
61885ba315a1SDustin Sallings     if (getuid() == 0 || geteuid() == 0) {
61895ba315a1SDustin Sallings         if (username == 0 || *username == '\0') {
61905ba315a1SDustin Sallings             fprintf(stderr, "can't run as root without the -u switch\n");
61917d8344ebSDustin Sallings             exit(EX_USAGE);
61925ba315a1SDustin Sallings         }
61935ba315a1SDustin Sallings         if ((pw = getpwnam(username)) == 0) {
61945ba315a1SDustin Sallings             fprintf(stderr, "can't find the user %s to switch to\n", username);
61957d8344ebSDustin Sallings             exit(EX_NOUSER);
61965ba315a1SDustin Sallings         }
61975ba315a1SDustin Sallings         if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
61985ba315a1SDustin Sallings             fprintf(stderr, "failed to assume identity of user %s\n", username);
61997d8344ebSDustin Sallings             exit(EX_OSERR);
62005ba315a1SDustin Sallings         }
62015ba315a1SDustin Sallings     }
62025ba315a1SDustin Sallings 
6203f1307c4dSDustin Sallings     /* Initialize Sasl if -S was specified */
6204f1307c4dSDustin Sallings     if (settings.sasl) {
6205f1307c4dSDustin Sallings         init_sasl();
6206f1307c4dSDustin Sallings     }
6207f1307c4dSDustin Sallings 
62087f241f91Sdormando     /* daemonize if requested */
62097f241f91Sdormando     /* if we want to ensure our ability to dump core, don't chdir to / */
621008c14e4eSTrond Norbye     if (do_daemonize) {
6211ee0c3d5aSTrond Norbye         if (sigignore(SIGHUP) == -1) {
6212ee0c3d5aSTrond Norbye             perror("Failed to ignore SIGHUP");
6213ee0c3d5aSTrond Norbye         }
6214ee0c3d5aSTrond Norbye         if (daemonize(maxcore, settings.verbose) == -1) {
62157f241f91Sdormando             fprintf(stderr, "failed to daemon() in order to daemonize\n");
62167d8344ebSDustin Sallings             exit(EXIT_FAILURE);
62177f241f91Sdormando         }
62187f241f91Sdormando     }
62197f241f91Sdormando 
622072a2eb4dSTomash Brechko     /* lock paged memory if needed */
622172a2eb4dSTomash Brechko     if (lock_memory) {
622272a2eb4dSTomash Brechko #ifdef HAVE_MLOCKALL
622372a2eb4dSTomash Brechko         int res = mlockall(MCL_CURRENT | MCL_FUTURE);
622472a2eb4dSTomash Brechko         if (res != 0) {
622572a2eb4dSTomash Brechko             fprintf(stderr, "warning: -k invalid, mlockall() failed: %s\n",
622672a2eb4dSTomash Brechko                     strerror(errno));
622772a2eb4dSTomash Brechko         }
622872a2eb4dSTomash Brechko #else
622972a2eb4dSTomash Brechko         fprintf(stderr, "warning: -k invalid, mlockall() not supported on this platform.  proceeding without.\n");
623072a2eb4dSTomash Brechko #endif
623172a2eb4dSTomash Brechko     }
623272a2eb4dSTomash Brechko 
623356b8339eSSteven Grimm     /* initialize main thread libevent instance */
623456b8339eSSteven Grimm     main_base = event_init();
623586969ea4SBrad Fitzpatrick 
6236ed0f5b0eSBrion Vibber     /* initialize other stuff */
6237916fff36Sdormando     logger_init();
6238ed0f5b0eSBrion Vibber     stats_init();
62391db1de38Sdormando     assoc_init(settings.hashpower_init);
6240ed0f5b0eSBrion Vibber     conn_init();
6241d7fb022dSdormando     slabs_init(settings.maxbytes, settings.factor, preallocate,
6242d7fb022dSdormando             use_slab_sizes ? slab_sizes : NULL);
624386969ea4SBrad Fitzpatrick 
6244f4242fdfSAnatoly Vorobey     /*
6245f4242fdfSAnatoly Vorobey      * ignore SIGPIPE signals; we can use errno == EPIPE if we
6246f4242fdfSAnatoly Vorobey      * need that information
6247f4242fdfSAnatoly Vorobey      */
6248ee0c3d5aSTrond Norbye     if (sigignore(SIGPIPE) == -1) {
62490a821dbbSBrad Fitzpatrick         perror("failed to ignore SIGPIPE; sigaction");
62507d8344ebSDustin Sallings         exit(EX_OSERR);
6251f4242fdfSAnatoly Vorobey     }
625256b8339eSSteven Grimm     /* start up worker threads if MT mode */
6253434c7cc5Sdormando     memcached_thread_init(settings.num_threads, main_base);
62547f09e20bSTrond Norbye 
62557f09e20bSTrond Norbye     if (start_assoc_maintenance_thread() == -1) {
62567f09e20bSTrond Norbye         exit(EXIT_FAILURE);
62577f09e20bSTrond Norbye     }
62587f09e20bSTrond Norbye 
6259c1f0262eSdormando     if (start_lru_crawler && start_item_crawler_thread() != 0) {
6260c1f0262eSdormando         fprintf(stderr, "Failed to enable LRU crawler thread\n");
6261c1f0262eSdormando         exit(EXIT_FAILURE);
6262c1f0262eSdormando     }
6263c1f0262eSdormando 
6264c1f0262eSdormando     if (start_lru_maintainer && start_lru_maintainer_thread() != 0) {
6265c1f0262eSdormando         fprintf(stderr, "Failed to enable LRU maintainer thread\n");
6266c1f0262eSdormando         return 1;
6267c1f0262eSdormando     }
6268c1f0262eSdormando 
626910698baeSdormando     if (settings.slab_reassign &&
627010698baeSdormando         start_slab_maintenance_thread() == -1) {
627110698baeSdormando         exit(EXIT_FAILURE);
627210698baeSdormando     }
627310698baeSdormando 
627483ba6bd9SJay Grizzard     if (settings.idle_timeout && start_conn_timeout_thread() == -1) {
627583ba6bd9SJay Grizzard         exit(EXIT_FAILURE);
627683ba6bd9SJay Grizzard     }
627783ba6bd9SJay Grizzard 
6278c9607c6dSBrad Fitzpatrick     /* initialise clock event */
6279c9607c6dSBrad Fitzpatrick     clock_handler(0, 0, 0);
62802439472aSBrian Aker 
62812439472aSBrian Aker     /* create unix mode sockets after dropping privileges */
62822439472aSBrian Aker     if (settings.socketpath != NULL) {
6283b2943f40Sdormando         errno = 0;
62842439472aSBrian Aker         if (server_socket_unix(settings.socketpath,settings.access)) {
62853635549eSDustin Sallings             vperror("failed to listen on UNIX socket: %s", settings.socketpath);
62867d8344ebSDustin Sallings             exit(EX_OSERR);
628756b8339eSSteven Grimm         }
628856b8339eSSteven Grimm     }
62892439472aSBrian Aker 
62902439472aSBrian Aker     /* create the listening socket, bind it, and init */
62912439472aSBrian Aker     if (settings.socketpath == NULL) {
6292e337faf6STrond Norbye         const char *portnumber_filename = getenv("MEMCACHED_PORT_FILENAME");
629361269bbdSGuillaume Delacour         char *temp_portnumber_filename = NULL;
629461269bbdSGuillaume Delacour         size_t len;
6295e337faf6STrond Norbye         FILE *portnumber_file = NULL;
6296e337faf6STrond Norbye 
6297e337faf6STrond Norbye         if (portnumber_filename != NULL) {
629861269bbdSGuillaume Delacour             len = strlen(portnumber_filename)+4+1;
629961269bbdSGuillaume Delacour             temp_portnumber_filename = malloc(len);
6300e337faf6STrond Norbye             snprintf(temp_portnumber_filename,
630161269bbdSGuillaume Delacour                      len,
6302e337faf6STrond Norbye                      "%s.lck", portnumber_filename);
6303e337faf6STrond Norbye 
6304e337faf6STrond Norbye             portnumber_file = fopen(temp_portnumber_filename, "a");
6305e337faf6STrond Norbye             if (portnumber_file == NULL) {
6306e337faf6STrond Norbye                 fprintf(stderr, "Failed to open \"%s\": %s\n",
6307e337faf6STrond Norbye                         temp_portnumber_filename, strerror(errno));
6308e337faf6STrond Norbye             }
6309e337faf6STrond Norbye         }
6310e337faf6STrond Norbye 
6311b2943f40Sdormando         errno = 0;
6312c605b31fSTrond Norbye         if (settings.port && server_sockets(settings.port, tcp_transport,
6313e337faf6STrond Norbye                                            portnumber_file)) {
63143635549eSDustin Sallings             vperror("failed to listen on TCP port %d", settings.port);
63157d8344ebSDustin Sallings             exit(EX_OSERR);
63162439472aSBrian Aker         }
63174b2326baSDustin Sallings 
63182439472aSBrian Aker         /*
63192439472aSBrian Aker          * initialization order: first create the listening sockets
63202439472aSBrian Aker          * (may need root on low ports), then drop root if needed,
63212439472aSBrian Aker          * then daemonise if needed, then init libevent (in some cases
63222439472aSBrian Aker          * descriptors created by libevent wouldn't survive forking).
63232439472aSBrian Aker          */
63242439472aSBrian Aker 
63252439472aSBrian Aker         /* create the UDP listening socket and bind it */
6326b2943f40Sdormando         errno = 0;
6327b8c40b08Sdormando         if (settings.udpport && server_sockets(settings.udpport, udp_transport,
6328e337faf6STrond Norbye                                               portnumber_file)) {
6329b8c40b08Sdormando             vperror("failed to listen on UDP port %d", settings.udpport);
63307d8344ebSDustin Sallings             exit(EX_OSERR);
63312439472aSBrian Aker         }
6332e337faf6STrond Norbye 
6333e337faf6STrond Norbye         if (portnumber_file) {
6334e337faf6STrond Norbye             fclose(portnumber_file);
6335e337faf6STrond Norbye             rename(temp_portnumber_filename, portnumber_filename);
633661269bbdSGuillaume Delacour             free(temp_portnumber_filename);
6337e337faf6STrond Norbye         }
63382439472aSBrian Aker     }
63392439472aSBrian Aker 
6340d1f9d992Sdormando     /* Give the sockets a moment to open. I know this is dumb, but the error
6341d1f9d992Sdormando      * is only an advisory.
6342d1f9d992Sdormando      */
6343d1f9d992Sdormando     usleep(1000);
6344cb01d504Sdormando     if (stats_state.curr_conns + stats_state.reserved_fds >= settings.maxconns - 1) {
6345d1f9d992Sdormando         fprintf(stderr, "Maxconns setting is too low, use -c to increase.\n");
6346d1f9d992Sdormando         exit(EXIT_FAILURE);
6347d1f9d992Sdormando     }
6348d1f9d992Sdormando 
6349af48b100STrond Norbye     if (pid_file != NULL) {
6350af48b100STrond Norbye         save_pid(pid_file);
6351af48b100STrond Norbye     }
6352af48b100STrond Norbye 
635369aa5427STrond Norbye     /* Drop privileges no longer needed */
635469aa5427STrond Norbye     drop_privileges();
635569aa5427STrond Norbye 
635656b8339eSSteven Grimm     /* enter the event loop */
6357538919d8Sdormando     if (event_base_loop(main_base, 0) != 0) {
6358538919d8Sdormando         retval = EXIT_FAILURE;
6359538919d8Sdormando     }
63607f09e20bSTrond Norbye 
63617f09e20bSTrond Norbye     stop_assoc_maintenance_thread();
63627f09e20bSTrond Norbye 
6363032912ceSLisa Seelye     /* remove the PID file if we're a daemon */
636408c14e4eSTrond Norbye     if (do_daemonize)
6365032912ceSLisa Seelye         remove_pidfile(pid_file);
6366a61a6900SBrian Aker     /* Clean up strdup() call for bind() address */
6367a61a6900SBrian Aker     if (settings.inter)
6368a61a6900SBrian Aker       free(settings.inter);
63699150c85bSBrian Aker     if (l_socket)
63709150c85bSBrian Aker       free(l_socket);
63719150c85bSBrian Aker     if (u_socket)
63729150c85bSBrian Aker       free(u_socket);
6373a61a6900SBrian Aker 
6374538919d8Sdormando     return retval;
637532f382b6SBrad Fitzpatrick }
6376