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